addition of silc.css
authorLubomir Sedlacik <salo@silcnet.org>
Tue, 6 Nov 2001 23:22:29 +0000 (23:22 +0000)
committerLubomir Sedlacik <salo@silcnet.org>
Tue, 6 Nov 2001 23:22:29 +0000 (23:22 +0000)
725 files changed:
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/COPYING [new file with mode: 0644]
apps/irssi/Makefile.am
apps/irssi/autogen.sh
apps/irssi/config
apps/irssi/config.h.in [deleted file]
apps/irssi/configure.in
apps/irssi/curses.m4 [deleted file]
apps/irssi/default-config.h [deleted file]
apps/irssi/default.theme
apps/irssi/docs/Makefile.am
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/invitelist.in [deleted file]
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/perlflush.in [deleted file]
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/irssi-version.h.in [deleted file]
apps/irssi/irssi.spec.in
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.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/memdebug.c [new file with mode: 0644]
apps/irssi/src/core/memdebug.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.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-redirect.c [new file with mode: 0644]
apps/irssi/src/core/servers-redirect.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/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/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/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/Makefile.am
apps/irssi/src/fe-text/gui-windows.c
apps/irssi/src/fe-text/silc.c
apps/irssi/src/fe-text/textbuffer-view.c
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/clientconfig.c [new file with mode: 0644]
apps/irssi/src/silc/core/clientconfig.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_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]
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-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-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-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/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/silcincludes.h
includes/silcwin32.h [moved from lib/silccrypt/e2_internal.h with 52% similarity]
includes/version.h
lib/LIBINDEX [new file with mode: 0644]
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/silcclient/DIRECTORY [new file with mode: 0644]
lib/silcclient/Makefile.am [moved from doc/Makefile.am with 56% similarity]
lib/silcclient/README [new file with mode: 0644]
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_prvmsg.c [new file with mode: 0644]
lib/silcclient/command.c [new file with mode: 0644]
lib/silcclient/command.h [moved from apps/silc/command.h with 59% similarity]
lib/silcclient/command_reply.c [new file with mode: 0644]
lib/silcclient/command_reply.h [moved from apps/silc/command_reply.h with 73% 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/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/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/silcmode.h [new file with mode: 0644]
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/silcpayload.c [new file with mode: 0644]
lib/silccore/silcpayload.h [new file with mode: 0644]
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/silcmath/silcprimegen.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 [new file with mode: 0644]
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 79% 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 68% similarity]
lib/silcutil/silcconfig.c [moved from lib/silccore/silcconfig.c with 89% similarity]
lib/silcutil/silcconfig.h [moved from lib/silccore/silcconfig.h with 100% similarity]
lib/silcutil/silchashtable.c [new file with mode: 0644]
lib/silcutil/silchashtable.h [new file with mode: 0644]
lib/silcutil/silclog.c [new file with mode: 0644]
lib/silcutil/silclog.h [moved from lib/silccore/silclog.h with 64% similarity]
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/unix/Makefile.am [new file with mode: 0644]
lib/silcutil/unix/silcunixmutex.c [new file with mode: 0644]
lib/silcutil/unix/silcunixnet.c [moved from lib/silccore/silcnet.c with 59% similarity]
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]
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/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/history.php [new file with mode: 0644]
public_html/html/install.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/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_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 [new file with mode: 0644]
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/trq_conf.h [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..fe6c148
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,5939 @@
+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..c43ad28
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,66 @@
+----------------------------------------------------------------------------
+
+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: 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
+
+----------------------------------------------------------------------------
diff --git a/INSTALL b/INSTALL
index c2d5f4b5028c088df3b11bf4f4bfe7027ec22d16..de90a1180a72fe101a1df93bde40528430533d19 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.
+
+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..5c0696c
--- /dev/null
@@ -0,0 +1,88 @@
+#
+#  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 = 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:
+       -cp -fR $(srcdir)/lib/silcsim/modules/*.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)/
+
+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
+install-data-hook: install-dirs generate-server-key sim-install doc-install etc-install
+endif
diff --git a/Makefile.defines.pre b/Makefile.defines.pre
new file mode 100644 (file)
index 0000000..af48710
--- /dev/null
@@ -0,0 +1,68 @@
+#
+#  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 \
+       -I$(silc_top_srcdir)/lib/trq
+
+#
+#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..3c0f45d
--- /dev/null
@@ -0,0 +1,207 @@
+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 SILC client.  There can be some extra files that will
+        never appear in public distribution, such as, configuration files.
+
+  silcd/
+
+        Includes SILC server.  There can be some extra files that will
+        never appear in public distribution, such as, configuration files.
+
+
+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
+
+Do not give the -d options if you do not want to dump the debugging.
+
+
+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..f8506a6d64ec3313bb340015f3d488c99ad4e841 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 /NAMES kees showing things wrong after JOIN and after ppl has left
+   channel.
 
-Feel free to contribute if you have the ability and free time - all the
-help is really appreciated - and needed.
+ 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.
 
-                                                       - Pekka
+ o JOINing to +a (requires passphrase to JOIN) does not work on autojoin.
+   Seems the passwords in the .silc/config has no effect.
 
-[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 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...
 
+ o The JOIN command's HELP is generated from Irssi IRCs JOIN help and
+   the syntax is not same in SILC.  This must be fixed.  Most likely
+   we must forget the Irssi's JOIN command and mimic it to get our
+   required syntax for it too.
 
-New features TODO
-=================
+ o We should get rid of the clientconfig.[ch] in Irssi SILC and move the
+   cipher, hash, hmac and pkcs configuration to the Irssi SILC's config
+   file.
 
- 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.
+ o Add PERL scripting support from Irssi CVS.
 
-   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 Extend the /HELP command to support sub commands or something.  So
+   that user can say /help set mutual_authentication they would get
+   help of the mutual_authentication setting.
 
-   This maybe post 1.0 task - dunno.
+ o Set different kind of settings, like, /set mutual_authentication,
+   /set key_exchange_timeout, /set conn_auth_timeout etc etc.
 
- 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 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.
+TODO/bugs In SILC Client Library
+================================
 
- 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 JOIN command's argument handling is buggy.  See the XXX in the code.
 
- 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.
+ o key agreement with itself causes the packet sequence numbers go grazy.
 
 
-TODO In SILC Client
-===================
+TODO/bugs In SILC Server
+========================
 
- 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 After backup resume protocol the TOPIC_SET was not handled correctly
+   by all (unknown Channel ID).
 
- 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 Channel user mode changes are notified unnecessarely when switching
+   to backup router on router crash.
 
- o Finish WHOIS, finish JOIN and other commands that are partly
-   implemented.
+ o Change the server to connect to another server from low ports (706)
+   and not from high ports.  Currently we cannot do incoming connection
+   checking by remote port because the port is not fixed.
 
- o Input line on UI is buggy.  Cursor movement etc bugs.  Too lazy to
-   fix it.
+ o Announcements are incomplete: channel topics are not announced,
+   user modes (UMODE) are not announced.
 
- 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 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 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 Optimize the WHOIS and IDENTIFY commands to somehow check whether the
+   requested clients are on some channel that the server knows about.  If
+   this is the case then the request is not needed to be forwarded to the
+   router.  One specific optimization could be done with JOIN command.
+   If the previous command to the WHOIS and IDENTIFY commands are JOIN
+   command (from the client) it can be expected (though it must be
+   verified) that the client is resolving the users on the channel it just
+   joined.  If server has done this once there is really no reason to
+   resolve it twice (from the router), it can reply directly back with
+   the information it knows.  This is because the server would (will) 
+   receive notifications from the router for users that are on a local
+   channel.
 
- 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.
+   The same is with whowas command.  Actually with all these commands
+   it should be checked also whether the requested information is local.
+   If it is, there is no reason to send it to the router, since the server
+   knows it best.
 
- 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 support for sending the LIST command to primary router on normal
+   server to receive all the created channels.  Currently the command
+   returns only the channels the server knows about.  The protocol spec
+   does not prohibit of sending the LIST to the router.
 
- 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 Incomplete IPv6 support:
 
- 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 silcd/serverid.c and its routines supports only IPv4.
 
- o Implement /KEYMAP (or similiar) command to remap control and function
-   keys.
+ o New configuration file format must be added.  The new one will be
+   done using the dotconf config library (lib/dotconf).  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 Implement /ALIAS command to make command aliases.
+       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 Implement /set/if/do/while etc as in IRC2.  Maybe post 1.0 task.
-   Some scripting would be good.
+       o Protocol execution timeouts are hard coded, should be
+         configurable.
 
- o Connection Authentication request resolving is missing and must be
-   done.  This is required by the protocol.
+       o IP address fields in configuration file should accept mask
+         format as well, IP/MASK, and not just plain IP.
 
- 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 Connection classes should be actually implemented in
+         serverconfig.c.  They can be defined but they are totally
+         ignored currently.  And they should be redefined also.
 
- 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.
+TODO/bugs In SILC Libraries
+===========================
 
- o Write help files for commands.  Nice format for the help files should
-   be selected.  I'm open for ideas.
+ o Optimizations to lib/silcsftp
 
- o All allocations and freeing needs to be checked for memory leaks.
-   Also, return values from various allocations and functions needs to
-   checked.
+       o Do not allocate new req for every client request.  Use
+         preallocated requests and recycle them.
 
+       o Use SilcList instead of SilcDList for requests.  It is faster.
 
-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 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 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 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 Command flag usage in general is not implemented yet.
-
- o Client history must be implemented.  Protocol says that server must
-   keep history information about clients for some period of time.
-
- 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 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 serverutil.c I guess should be created for util-like functions that
-   now resides in server.c, which is getting too big.
-
- 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.
-
- o Implement REDIRECT_CLIENT section in serverconfig.c and in server.
-
- o Configuration file format - could be better.
-
- o IP address fields in configuration file should accept mask format
-   as well, IP/MASK, and not just plain IP.
-
- 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 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 Do not allocate new buffer for every packet.  Use preallocated
+         buffer and reallocate only if necessary.
 
  o Compression routines are missing.  The protocol supports packet
    compression thus it must be implemented.  SILC Comp API must be
@@ -263,84 +132,46 @@ TODO In SILC Libraries
    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 All payload parsing (decoding) functions should take unsigned char *
+   and uint32 as data and data length as arguments.  Now some of the
+   routines do already that but most of the routines use SilcBuffer.
+   The SilcBuffer ones should be removed since buf->data and buf->len
+   is more convenient to use.  These are currently only cosmetic changes
+   but at some point must be done to make the payload interfaces
+   consistent.
 
- 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 Incomplete IPv6 support:
 
- o I don't like the ID cache system currenly implemented.  Ugly and
-   not so good.  Must be rewritten very soon.
+       o All network routines in lib/silcutil/silcnet.[ch] does not
+         support IPv6.
+       o silc_id_render supports only IPv4 based ID's in the file
+         lib/silcutil/silcutil.c.
 
- o All allocations and freeing needs to be checked for memory leaks.
+ 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.
 
- 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.
+TODO/Bugs in native WIN32 support (libraries)
+=============================================
 
+ o 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.
 
-Other Things TODO
-=================
 
- o Write manuals for server.
+TODO In SILC Protocol
+=====================
 
- 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.
+ o If channel founder mode is set and the invite mode is set on channel
+   then the founder should be added to the list automatically so that
+   if the founder signoff's it will be able join again to the invite only
+   channel wihtout being invited.
 
 
 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.
-
-   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 X.509 certificate support.  SILC protocol supports certificates and
    it would be great to have support for them.  This is a big task as
    support has to be made for ASN.1 as well.  I've looked into OpenSSL 
@@ -356,14 +187,10 @@ TODO After 1.0
    to start writing one myself.  Anyhow, the OpenSSL X.509 lib should
    be checked.
 
+   Other package that should be checked is the NSS's X509 library.
+
  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.
 
- 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 Cipher optimizations (asm, that this) at least for i386 would be nice.
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..a1649da
--- /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
+
+/* Default pid file */
+#undef SILC_SERVER_PID_FILE
+
+/* Multi-thread support */
+#undef SILC_THREADS
+#undef SILC_HAVE_PTHREAD
+
+/* 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/COPYING b/apps/irssi/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.
index 304c44d868e1715483b63b7faca403ec7ded0092..a8a1eff4a59bbf8e340cab5ba9030bb94e7d613d 100644 (file)
@@ -8,21 +8,24 @@ default-theme.h: $(srcdir)/default.theme
 
 SUBDIRS = src docs
 
-confdir = $(sysconfdir)/irssi
+include $(top_srcdir)/Makefile.defines.in
+
+#confdir = $(sysconfdir)/irssi
+confdir = $(silc_etcdir)
 conf_DATA = config default.theme
 
 noinst_HEADERS = irssi-version.h
 
 EXTRA_DIST = \
        autogen.sh \
-       curses.m4 \
        README \
        file2header.sh \
        irssi.spec \
        irssi.spec.in \
        $(conf_DATA) \
        irssi-config.in \
-       irssi-icon.png
+       Makefile.defines.in \
+       Makefile.defines_int.in
 
 ## make rpms
 rpm: Makefile
index daf8a990b18d860fd8f43e47ca5d51c34f4b5997..d968658d38002ab464b0a03f81c36004d9aaa722 100755 (executable)
@@ -88,9 +88,9 @@ esac
 rm -f aclocal.m4
 if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
   echo "Running libtoolize..."
-  libtoolize --force --copy
+  libtoolize --copy --force
 fi
-aclocalinclude="$ACLOCAL_FLAGS -I ."
+aclocalinclude="$ACLOCAL_FLAGS"
 echo "Running aclocal $aclocalinclude ..."
 aclocal $aclocalinclude
 if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
@@ -99,7 +99,5 @@ if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
 fi
 echo "Running autoconf ..."
 autoconf
-echo "Running automake --gnu $am_opt ..."
-automake --add-missing --gnu $am_opt
-
-conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+echo "Running automake $am_opt ..."
+automake --add-missing --foreign $am_opt
index 876b402012972ecaae1adb0d9b34887321084439..b930de3d68f06b15c8ce49d3869271d01fe144e6 100644 (file)
@@ -1,33 +1,18 @@
 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; }
+  { address = "silc.silcnet.org"; chatnet = SILCNet; 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"; };
+  SILCNet = { type = "SILC"; };
 };
 
 channels = (
-  { name = "#irssi"; chatnet = ircnet; autojoin = No; },
-  { name = "#irssi"; chatnet = opn; autojoin = No; },
-  { name = "#silc"; chatnet = silc; autojoin = No; }
+  { name = "#silc"; chatnet = silcnet; autojoin = No; }
 );
 
 aliases = {
-  J = "join";
-  WJOIN = "join -window";
-  WQUERY = "query -window";
+  JOIN = "join -window";
+  QUERY = "query -window";
   LEAVE = "part";
   BYE = "quit";
   EXIT = "quit";
@@ -37,6 +22,7 @@ aliases = {
   HOST = "userhost";
   LAST = "lastlog";
   SAY = "msg *";
+  WHO = "users *";
   WI = "whois";
   WII = "whois $0 $0";
   WW = "whowas";
@@ -56,10 +42,22 @@ aliases = {
   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";
+  ADMIN = "info";
+};
+
+settings = {
+  "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";
+    use_auto_addr = "no";
+  };
+  "fe-text" = { topicbar = "no"; mail_counter = "yes"; indent = "8"; };
 };
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..dfc21b47d04a45fa86f34b928f9fd501ef7aa5f6 100644 (file)
@@ -1,7 +1,7 @@
 AC_INIT(src)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(Irssi SILC, 0.7.98.3)
+AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.98.3)
 
 AM_MAINTAINER_MODE
 
@@ -10,7 +10,7 @@ AC_PROG_CC
 AC_PROG_CPP
 AC_STDC_HEADERS
 AC_ARG_PROGRAM
-AM_PROG_LIBTOOL
+AC_PROG_RANLIB
 
 dnl * ahem.. :) we don't want static libraries for modules
 if test "x$lt_target" = "x"; then
@@ -25,12 +25,7 @@ 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_CONFIG_AUX_DIR(.)
 
 AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h)
 
@@ -128,7 +123,7 @@ AC_ARG_ENABLE(perl-path,
                        perl_lib_dir_given=yes
                fi
        fi,
-       want_perl=yes)
+       want_perl=no)
 
 AC_ARG_ENABLE(perl,
 [  --enable-perl[=yes|no|static]  Build with Perl support - also specifies
@@ -141,7 +136,7 @@ AC_ARG_ENABLE(perl,
        else
                want_perl=no
        fi,
-       want_perl=yes)
+       want_perl=no)
 
 AC_ARG_WITH(tests,
 [  --with-tests           Run all the tests],
@@ -352,64 +347,302 @@ fi
 PROG_LIBS="$PROG_LIBS $GLIB_LIBS"
 
 dnl **
-dnl ** check if we can link dynamic libraries to modules
-dnl ** also checks if libraries are built to .libs dir
+dnl ** curses checks
 dnl **
 
-AC_MSG_CHECKING([if we can link dynamic libraries with modules])
-DYNLIB_MODULES=no
+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
 
-dnl ** compile object file
-cat > conftest.c <<EOF
-#include <math.h>
-int modfunc(void){return (int)floor(1.2);}
-EOF
+       CFLAGS=${CFLAGS--O}
 
-./libtool --mode=compile $CC $CFLAGS -c conftest.c 2> /dev/null > /dev/null
-if test ! -s conftest.lo; then
-  AC_ERROR([error compiling test module])
-fi
+       AC_SUBST(CURSES_LIBS)
+       AC_SUBST(CURSES_INCLUDEDIR)
 
-dnl ** link to library
-./libtool --mode=link $CC $CFLAGS $LDFLAGS -rpath /usr/lib conftest.lo -lm -o libconftest.la > /dev/null
-if test ! -s .libs/libconftest.a; then
-  AC_ERROR([error, can't even find .a library])
-fi
+       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
+       ])
 
-dnl ** check if dynamic linking worked
-libfile=`grep '^library_names' libconftest.la|$sedpath "s/library_names='\(.*\)'.*/\1/"|$sedpath 's/.* \([[^ ]]*\)$/\1/'`
-if test ! -s .libs/$libfile; then
-  AC_MSG_RESULT([no, error linking test module])
-else
-  cat > conftest.c <<EOF
-#include <gmodule.h>
-main() {
-GModule *m; int (*modfunc)(void);
-m = g_module_open(".libs/$libfile", 0);
-if (!m) g_print("error loading: %s", g_module_error());
-else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc))
-  g_print("modfunc() symbol not found from module");
-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
-  if test ! -s conftest; then
-    AC_MSG_RESULT([no, error compiling test program])
-  else
-    status="`./conftest`"
-    if test "x$status" = "xok"; then
-      DYNLIB_MODULES=yes
-      AC_MSG_RESULT([yes])
-    else
-      AC_MSG_RESULT([no, error running: $status])
+       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 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_WARN(Use of the bsdcurses extension has some)
+dnl    AC_WARN(display/input problems.)
+dnl    AC_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"
+           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
-  fi
-fi
-rm -rf conftest conftest.* libconftest.* .libs
+])
 
-dnl **
-dnl ** curses checks
-dnl **
+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
+])
 
 if test "x$want_textui" = "xyes"; then
        AC_CHECK_CURSES
@@ -647,12 +880,13 @@ 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
+       echo "void ${c}_core_init(void); void ${c}_core_init_finish(void); void ${c}_core_deinit(void);" >> $file
        if test "x$module_inits" != "x"; 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
         echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file
+        echo "void ${c}_init_finish(void) { ${c}_core_init_finish(); $module_inits }" >> $file
         echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file
 
        if test -f $srcdir/src/fe-common/$c/module.h; then
@@ -700,6 +934,9 @@ if test "x$want_ipv6" = "xyes"; then
        AC_DEFINE(HAVE_IPV6)
 fi
 
+INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"
+AC_SUBST(INCLUDE_DEFINES_INT)
+
 AC_OUTPUT(
 Makefile
 src/Makefile
@@ -781,4 +1018,3 @@ if test "x$want_perl" = "xyes"; then
        fi
 fi
 echo "Install prefix ............. : $prefix"
-
diff --git a/apps/irssi/curses.m4 b/apps/irssi/curses.m4
deleted file mode 100644 (file)
index 7865b1c..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-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
-               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 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_WARN(Use of the bsdcurses extension has some)
-dnl    AC_WARN(display/input problems.)
-dnl    AC_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"
-           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
-])
-
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..4e1ec830900776a875a5d5c96189603978f50a88 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,109 +154,78 @@ 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}";
-
-  ##
-  ## 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-}";
+  privmsgnick = "*%c$0%n* ";
 
-  # 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_nick = "[ %n%_$0%_$1- ] ";
+  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
@@ -257,4 +248,15 @@ abstracts = {
   sbact = "{sb {sbact_act $0}{sbact_det $1}}";
   sbact_act = "Act: $0-";
   sbact_det = " Det: $0-";
+
+};
+
+#
+# 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}";
+  };
 };
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)
 
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..495ec0e
--- /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..38b63b4
--- /dev/null
@@ -0,0 +1,9 @@
+
+@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.  This way the public key might
+have been verified already.  However, you will be prompted to verify
+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/invitelist.in b/apps/irssi/docs/help/in/invitelist.in
deleted file mode 100644 (file)
index ed6bc06..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:invitelist@
-
-Shows the +I modes of the current channel. +I mode
-allows free joins of clients with certain userhost mask
-even if the channel is invite only.
-
-See also: INVITE, MODE
-
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..424362afd7e601603d37132e2be7a9a633329d91 100644 (file)
@@ -2,10 +2,9 @@
 @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)
+JOIN is aliased to J by default.
 
 Description
 
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..b4874c9
--- /dev/null
@@ -0,0 +1,95 @@
+
+@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
+      this variable 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 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, 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
 
diff --git a/apps/irssi/docs/help/in/perlflush.in b/apps/irssi/docs/help/in/perlflush.in
deleted file mode 100644 (file)
index 27fc509..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:perlflush@
-
-Stops and removes all Perl scripts which have been run.
-Also undefines all the commands defined by Perl scripts.
-
-See also: RUN
-
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..0d2137ba422fb0b99d82ca5dc802dc13cad83c32 100644 (file)
@@ -1,8 +1,8 @@
 
 @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.
 
-See also: OPER, SQUIT, RESTART
+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
-
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..40f1b21e3672eb41410ae5d39465b4c052d446b0 100644 (file)
@@ -109,8 +109,8 @@ rm -rf $RPM_BUILD_ROOT
 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.1  2001/05/24 12:09:28  priikone
+Initial revision
 
 Revision 1.11  2001/03/29 14:38:28  cras
 http://irssi.org -> http://irssi.org/
diff --git a/apps/irssi/src/core/Makefile.am b/apps/irssi/src/core/Makefile.am
new file mode 100644 (file)
index 0000000..1443bc9
--- /dev/null
@@ -0,0 +1,104 @@
+noinst_LIBRARIES = libcore.a
+
+include $(top_srcdir)/Makefile.defines.in
+
+INCLUDES = \
+       $(GLIB_CFLAGS) \
+       -DSYSCONFDIR=\""$(silc_etcdir)"\" \
+       -DMODULEDIR=\""$(silc_modulesdir)"\" \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core
+
+if BUILD_MEMDEBUG
+memdebug_src=memdebug.c
+else
+memdebug_src=
+endif
+
+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 \
+       masks.c \
+        $(memdebug_src) \
+       misc.c \
+       modules.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-redirect.c \
+       servers-setup.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 \
+        memdebug.h \
+       misc.h \
+       module.h \
+       modules.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-redirect.h \
+       servers-setup.h \
+       settings.h \
+       signals.h \
+       special-vars.h \
+       window-item-def.h \
+       write-buffer.h \
+       $(structure_headers)
+
+EXTRA_DIST = \
+       memdebug.c
diff --git a/apps/irssi/src/core/args.c b/apps/irssi/src/core/args.c
new file mode 100644 (file)
index 0000000..093a8d5
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ 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;
+}
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..2ff6a53
--- /dev/null
@@ -0,0 +1,30 @@
+/* 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 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..a53bd3f
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ 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);
+       if (chatnet == NULL) /* FIXME: remove this after .98... */ {
+               chatnet = g_strdup(config_node_get_str(node, "ircnet", NULL));
+               if (chatnet != NULL) {
+                        iconfig_node_set_str(node, "chatnet", chatnet);
+                        iconfig_node_set_str(node, "ircnet", 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) {
+               for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+                       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..f3d3a7a
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ 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->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);
+       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);
+}
+
+/* connected to server, autojoin to channels. */
+static void event_connected(SERVER_REC *server)
+{
+       GString *chans;
+       GSList *tmp;
+
+       g_return_if_fail(SERVER(server));
+
+       if (server->connrec->reconnection)
+               return;
+
+       /* 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;
+
+               g_string_sprintfa(chans, "%s,", rec->name);
+       }
+
+       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));
+
+       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;
+
+               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..78a05a7
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ 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"
+
+static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr)
+{
+        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_free(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;
+
+        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;
+
+       conn = get_server_connect(data, NULL);
+        if (conn != NULL)
+               CHAT_PROTOCOL(conn)->server_connect(conn);
+}
+
+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;
+                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_reconnect_destroy(recon, FALSE);
+
+               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);
+
+       if (server != NULL) {
+               signal_emit("command disconnect", 2,
+                           "* Changing server", server);
+       } else {
+               server_connect_free(oldconn);
+       }
+}
+
+/* SYNTAX: SERVER [-4 | -6] [-ircnet <ircnet>] [-host <hostname>]
+                  [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
+static void cmd_server(const char *data, SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *conn;
+       int plus_addr;
+
+       g_return_if_fail(data != NULL);
+
+        /* create connection record */
+       conn = get_server_connect(data, &plus_addr);
+       if (conn != NULL) {
+               if (!plus_addr)
+                       update_reconnection(conn, server);
+               CHAT_PROTOCOL(conn)->server_connect(conn);
+       }
+}
+
+/* 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 (!IS_SERVER(server) || !server->connected)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &channels))
+               return;
+
+       if (g_hash_table_lookup(optlist, "invite"))
+               channels = server->last_invite;
+       else {
+               if (*channels == '\0')
+                       cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+               /* -<server tag> */
+               server = cmd_options_get_server("join", optlist, server);
+       }
+
+       if (server != NULL && channels != NULL)
+               server->channels_join(server, channels, FALSE);
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: MSG [-<server tag>] <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;
+
+       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;
+       } else if (strcmp(target, "*") == 0 && item != NULL)
+               target = item->name;
+
+       if (target != NULL)
+               server->send_message(server, target, msg);
+
+       signal_emit(target != NULL && server->ischannel(target) ?
+                   "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 *tmp;
+
+       for (tmp = servers; tmp != NULL; tmp = tmp->next)
+               signal_emit("send command", 3, data, tmp->data, NULL);
+}
+
+/* SYNTAX: FOREACH CHANNEL <command> */
+static void cmd_foreach_channel(const char *data)
+{
+       GSList *tmp;
+
+       for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *rec = tmp->data;
+
+               signal_emit("send command", 3, data, rec->server, rec);
+       }
+}
+
+/* SYNTAX: FOREACH QUERY <command> */
+static void cmd_foreach_query(const char *data)
+{
+       GSList *tmp;
+
+       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               signal_emit("send command", 3, data, rec->server, rec);
+       }
+}
+
+void chat_commands_init(void)
+{
+       settings_add_str("misc", "quit_message", "leaving");
+
+       command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
+       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);
+
+       command_set_options("connect", "4 6 +host");
+       command_set_options("join", "invite");
+}
+
+void chat_commands_deinit(void)
+{
+       command_unbind("server", (SIGNAL_FUNC) cmd_server);
+       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);
+}
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..ab7c09d
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ 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);
+}
+
+/* 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);
+}
+
+/* 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;
+
+       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..cc7eaad
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __CHAT_PROTOCOLS_H
+#define __CHAT_PROTOCOLS_H
+
+typedef struct {
+       int id;
+
+       unsigned int not_initialized: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);
+
+        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..a2cff52
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ 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;
+
+       while (chatnets != NULL)
+                chatnet_destroy(chatnets->data);
+
+       node = iconfig_node_traverse("chatnets", FALSE);
+       if (node == NULL) {
+               /* FIXME: remove after .98 */
+               node = iconfig_node_traverse("ircnets", FALSE);
+               if (node != NULL) {
+                       /* very dirty method - doesn't update hashtables
+                          but this will do temporarily.. */
+                       g_free(node->key);
+                        node->key = g_strdup("chatnets");
+               }
+       }
+
+       if (node != NULL)
+                g_slist_foreach(node->value, (GFunc) chatnet_read, NULL);
+}
+
+void chatnets_init(void)
+{
+       chatnets = NULL;
+
+       signal_add("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)
+{
+       while (chatnets != NULL)
+               chatnet_destroy(chatnets->data);
+       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..c4c8890
--- /dev/null
@@ -0,0 +1,911 @@
+/*
+ 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 "servers-redirect.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, 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)
+{
+        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);
+               rec->modules = g_slist_append(rec->modules, modrec);
+       }
+
+        return modrec;
+}
+
+void command_bind_to(const char *module, int pos, const char *cmd,
+                    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);
+
+        modrec->signals = g_slist_append(modrec->signals, 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);
+               modrec->signals = g_slist_remove(modrec->signals, 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);
+
+       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);
+
+       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 == '-' && isspace((*data)[1])) {
+                               /* -- option means end of options even
+                                  if next word starts with - */
+                               (*data)++;
+                               while (isspace(**data)) (*data)++;
+                               break;
+                       }
+
+                       if (!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 && !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)
+                               g_hash_table_insert(options, option, "");
+
+                       if (pos < 0 || !iscmdtype(*optlist[pos]) ||
+                           *optlist[pos] == '!')
+                               option = NULL;
+
+                       while (isspace(**data)) (*data)++;
+                       continue;
+               }
+
+               if (option == NULL)
+                       break;
+
+               if (*optlist[pos] == '@' && !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 (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)
+{
+        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 ||
+            !active_item->server->ischannel(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;
+
+       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 */
+                        arg = get_optional_channel(item, &datad);
+
+                       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, 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);
+       }
+}
+
+#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)
+{
+       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;
+       }
+
+       cmd = g_strconcat("command ", newcmd, NULL);
+       if (server != NULL)
+               server_redirect_default(SERVER(server), cmd);
+
+       g_strdown(cmd);
+       oldcmd = current_command;
+       current_command = cmd+8;
+       if (!signal_emit(cmd, 3, args, server, item)) {
+               signal_emit_id(signal_default_command, 3,
+                              command, server, item);
+       }
+       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..9dee2e8
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef __COMMANDS_H
+#define __COMMANDS_H
+
+#include "signals.h"
+
+typedef struct {
+       char *name;
+       char *options;
+        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 IRC 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_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,
+                    const char *category, SIGNAL_FUNC func);
+#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, b, c)
+#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, b, c)
+#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, b, c)
+
+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
+
+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..fd7459d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ 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 "pidwait.h"
+
+#include "net-disconnect.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "settings.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"
+
+void chat_commands_init(void);
+void chat_commands_deinit(void);
+
+int irssi_gui;
+
+void core_init(void)
+{
+       modules_init();
+#ifndef WIN32
+       pidwait_init();
+#endif
+
+       net_disconnect_init();
+       net_sendbuffer_init();
+       signals_init();
+       settings_init();
+       commands_init();
+        nickmatch_cache_init();
+
+       chat_protocols_init();
+       chatnets_init();
+        expandos_init();
+       ignore_init();
+       servers_init();
+        write_buffer_init();
+       log_init();
+       rawlog_init();
+
+       channels_init();
+       queries_init();
+       nicklist_init();
+
+       chat_commands_init();
+        settings_check();
+}
+
+void core_deinit(void)
+{
+       chat_commands_deinit();
+
+       nicklist_deinit();
+       queries_deinit();
+       channels_deinit();
+
+       rawlog_deinit();
+       log_deinit();
+        write_buffer_deinit();
+       servers_deinit();
+       ignore_deinit();
+        expandos_deinit();
+       chatnets_deinit();
+       chat_protocols_deinit();
+
+        nickmatch_cache_deinit();
+       commands_deinit();
+       settings_deinit();
+       signals_deinit();
+       net_sendbuffer_deinit();
+       net_disconnect_deinit();
+
+#ifndef WIN32
+       pidwait_deinit();
+#endif
+       modules_deinit();
+}
diff --git a/apps/irssi/src/core/core.h b/apps/irssi/src/core/core.h
new file mode 100644 (file)
index 0000000..61d6ef7
--- /dev/null
@@ -0,0 +1,17 @@
+#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;
+
+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..21ebae6
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ 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[127];
+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;
+
+#define CHAR_EXPANDOS_COUNT \
+       ((int) (sizeof(char_expandos) / sizeof(char_expandos[0])))
+
+/* 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_expandos[(int) *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) *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_expandos[(int) *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_expandos[(int) *key];
+               if (rec != NULL && rec->func == func) {
+                       char_expandos[(int) *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);
+       }
+}
+
+EXPANDO_FUNC expando_find_char(char chr)
+{
+       g_return_val_if_fail(chr < CHAR_EXPANDOS_COUNT, NULL);
+
+       return char_expandos[(int) chr] == NULL ? NULL :
+               char_expandos[(int) 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 (numeric version string) */
+static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret)
+{
+       return IRSSI_VERSION_DATE;
+}
+
+/* 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)
+{
+        signal_emit("expando timer", 0);
+        return 1;
+}
+
+static void read_settings(void)
+{
+        timestamp_format = settings_get_str("timestamp_format");
+}
+
+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;
+
+        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("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, 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(1000, (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 < CHAR_EXPANDOS_COUNT; 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..3dcb527
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __EXPANDOS_H
+#define __EXPANDOS_H
+
+#include "signals.h"
+
+/* first argument of signal must match to active .. */
+typedef enum {
+        EXPANDO_ARG_NONE,
+        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);
+
+/* 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..69460c5
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ 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 = (channel != NULL && server != NULL &&
+                  server->ischannel(channel)) ?
+               channel_find(server, channel) : NULL;
+       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));
+}
+
+void ignore_add_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
+
+       ignores = g_slist_append(ignores, rec);
+       ignore_set_config(rec);
+
+       signal_emit("ignore created", 1, rec);
+}
+
+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;
+       }
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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));
+               if (rec->mask != NULL && strcmp(rec->mask, "*") == 0) {
+                       /* FIXME: remove after .98 */
+                        g_free(rec->mask);
+                       rec->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);
+               if (*config_node_get_str(node, "except_level", "") != '\0') {
+                       /* FIXME: remove after .98 */
+                       rec->level = level2bits(config_node_get_str(node, "except_level", ""));
+                        rec->exception = TRUE;
+               }
+               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);
+       }
+
+       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..aae506a
--- /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",
+       "HILIGHT",
+
+       "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.c b/apps/irssi/src/core/log.c
new file mode 100644 (file)
index 0000000..368de25
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ 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 644
+
+#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;
+
+       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);
+               log_start_logging(log);
+       }
+       g_free(new_fname);
+}
+
+void log_write_rec(LOG_REC *log, const char *str, int level)
+{
+       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 ((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);
+}
+
+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(SERVER_REC *server, const char *item, int level,
+                   const char *str, int no_fallbacks)
+{
+       GSList *tmp, *fallbacks;
+       char *tmpstr, *servertag;
+       int found;
+
+       g_return_if_fail(str != NULL);
+
+       if (logs == NULL)
+               return;
+
+       servertag = server == NULL ? NULL : server->tag;
+       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,
+                                      servertag) != 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);
+}
+
+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;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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));
+
+               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();
+       log_read_config();
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+        signal_add("setup reread", (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);
+}
diff --git a/apps/irssi/src/core/log.h b/apps/irssi/src/core/log.h
new file mode 100644 (file)
index 0000000..7361b6a
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __LOG_H
+#define __LOG_H
+
+enum {
+       LOG_ITEM_TARGET, /* channel, query, .. */
+       LOG_ITEM_WINDOW_REFNUM
+};
+
+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 */
+
+       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(SERVER_REC *server, 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/memdebug.c b/apps/irssi/src/core/memdebug.c
new file mode 100644 (file)
index 0000000..213d454
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ memdebug.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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gmodule.h>
+
+/*#define ENABLE_BUFFER_CHECKS*/
+#define BUFFER_CHECK_SIZE 5
+#define MIN_BUFFER_CHECK_SIZE 2
+
+typedef struct {
+       void *p;
+       int size;
+       char *file;
+       int line;
+       char *comment;
+} MEM_REC;
+
+static GHashTable *data = NULL, *preallocs = NULL;
+static const char *comment = "";
+
+static void add_flow_checks(char *p, unsigned long size)
+{
+#ifdef ENABLE_BUFFER_CHECKS
+       int n;
+
+       for (n = 0; n < BUFFER_CHECK_SIZE; n++)
+               p[n] = n ^ 0x7f;
+       for (n = 0; n < BUFFER_CHECK_SIZE; n++)
+               p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f;
+#endif
+}
+
+void ig_memcheck_rec(void *key, MEM_REC *rec)
+{
+       guchar *p;
+       int n;
+
+       if (rec->size != INT_MIN){
+               p = rec->p;
+
+               for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
+                       if (p[n] != (n ^ 0x7f))
+                               g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line);
+
+               for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
+                       if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f))
+                               g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line);
+       }
+}
+
+static void mem_check(void)
+{
+#ifdef ENABLE_BUFFER_CHECKS
+       g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL);
+#endif
+}
+
+static void data_add(char *p, int size, const char *file, int line)
+{
+       MEM_REC *rec;
+
+       if (size <= 0 && size != INT_MIN)
+               g_error("size = %d, file %s line %d", size, file, line);
+
+       if (data == NULL) {
+               data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+               preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+       }
+
+       if (g_hash_table_lookup(data, p) != NULL)
+               g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line);
+
+       rec = g_new(MEM_REC, 1);
+       g_hash_table_insert(data, p, rec);
+
+       rec->p = p;
+       rec->size = size;
+       rec->file = g_strdup(file);
+       rec->line = line;
+       rec->comment = g_strdup(comment);
+
+       if (size == INT_MIN)
+               g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p);
+       else
+               add_flow_checks(p, size);
+       mem_check();
+}
+
+static void data_clear(char *p)
+{
+       MEM_REC *rec;
+
+       if (g_hash_table_lookup(preallocs, p) != NULL)
+               p += BUFFER_CHECK_SIZE;
+
+       rec = g_hash_table_lookup(data, p);
+       if (rec != NULL && rec->size > 0)
+               memset(p, 'F', rec->size);
+}
+
+static void *data_remove(char *p, const char *file, int line)
+{
+       MEM_REC *rec;
+
+       mem_check();
+
+       if (g_hash_table_lookup(preallocs, p) != NULL) {
+               g_hash_table_remove(preallocs, p);
+               p += BUFFER_CHECK_SIZE;
+       }
+
+       rec = g_hash_table_lookup(data, p);
+       if (rec == NULL) {
+               g_warning("data_remove() data %p not found (in %s:%d)", p, file, line);
+               return p+BUFFER_CHECK_SIZE;
+       }
+
+       g_hash_table_remove(data, p);
+       g_free(rec->file);
+       g_free(rec->comment);
+       g_free(rec);
+
+       return p;
+}
+
+void *ig_malloc(int size, const char *file, int line)
+{
+       char *p;
+
+       size += BUFFER_CHECK_SIZE*2;
+       p = g_malloc(size);
+       data_add(p, size, file, line);
+       return (void *) (p+BUFFER_CHECK_SIZE);
+}
+
+void *ig_malloc0(int size, const char *file, int line)
+{
+       char *p;
+
+       size += BUFFER_CHECK_SIZE*2;
+       p = g_malloc0(size);
+       data_add(p, size, file, line);
+       return (void *) (p+BUFFER_CHECK_SIZE);
+}
+
+void *ig_realloc(void *mem, unsigned long size, const char *file, int line)
+{
+       char *p, *oldmem = mem;
+
+       size += BUFFER_CHECK_SIZE*2;
+       oldmem -= BUFFER_CHECK_SIZE;
+       data_remove(oldmem, file, line);
+       p = g_realloc(oldmem, size);
+       data_add(p, size, file, line);
+       return (void *) (p+BUFFER_CHECK_SIZE);
+}
+
+char *ig_strdup(const char *str, const char *file, int line)
+{
+       void *p;
+
+       if (str == NULL) return NULL;
+
+       p = ig_malloc(strlen(str)+1, file, line);
+       strcpy(p, str);
+
+       return p;
+}
+
+char *ig_strndup(const char *str, int count, const char *file, int line)
+{
+       char *p;
+
+       if (str == NULL) return NULL;
+
+       p = ig_malloc(count+1, file, line);
+       strncpy(p, str, count); p[count] = '\0';
+
+       return p;
+}
+
+char *ig_strconcat(const char *file, int line, const char *str, ...)
+{
+  guint          l;
+  va_list args;
+  char   *s;
+  char   *concat;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  l = 1 + strlen (str);
+  va_start (args, str);
+  s = va_arg (args, char*);
+  while (s)
+    {
+      l += strlen (s);
+      s = va_arg (args, char*);
+    }
+  va_end (args);
+
+  concat = ig_malloc(l, file, line);
+  concat[0] = 0;
+
+  strcat (concat, str);
+  va_start (args, str);
+  s = va_arg (args, char*);
+  while (s)
+    {
+      strcat (concat, s);
+      s = va_arg (args, char*);
+    }
+  va_end (args);
+
+  return concat;
+}
+
+char *ig_strdup_printf(const char *file, int line, const char *format, ...)
+{
+       char *buffer, *p;
+       va_list args;
+
+       va_start (args, format);
+       buffer = g_strdup_vprintf (format, args);
+       va_end (args);
+
+       p = ig_malloc(strlen(buffer)+1, file, line);
+       strcpy(p, buffer);
+       g_free(buffer);
+
+       return p;
+}
+
+char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args)
+{
+       char *buffer, *p;
+
+       buffer = g_strdup_vprintf (format, args);
+
+       p = ig_malloc(strlen(buffer)+1, file, line);
+       strcpy(p, buffer);
+       g_free(buffer);
+
+       return p;
+}
+
+void ig_free(void *p)
+{
+       char *cp = p;
+
+       if (cp == NULL) g_error("ig_free() : trying to free NULL");
+
+       cp -= BUFFER_CHECK_SIZE;
+       data_clear(cp);
+       cp = data_remove(cp, "??", 0);
+       if (cp != NULL) g_free(cp);
+}
+
+GString *ig_string_new(const char *file, int line, const char *str)
+{
+       GString *ret;
+
+       ret = g_string_new(str);
+       data_add((void *) ret, INT_MIN, file, line);
+       return ret;
+}
+
+void ig_string_free(const char *file, int line, GString *str, gboolean freeit)
+{
+       data_remove((void *) str, file, line);
+       if (!freeit)
+               data_add(str->str, INT_MIN, file, line);
+
+       g_string_free(str, freeit);
+}
+
+char *ig_strjoinv(const char *file, int line, const char *sepa, char **array)
+{
+       char *ret;
+
+       ret = g_strjoinv(sepa, array);
+       data_add(ret, INT_MIN, file, line);
+       return ret;
+}
+
+char *ig_dirname(const char *file, int line, const char *fname)
+{
+       char *ret;
+
+       ret = g_dirname(fname);
+       data_add(ret, INT_MIN, file, line);
+       return ret;
+}
+
+char *ig_module_build_path(const char *file, int line, const char *dir, const char *module)
+{
+       char *ret;
+
+       ret = g_module_build_path(dir, module);
+       data_add(ret, INT_MIN, file, line);
+       return ret;
+}
+
+void ig_profile_line(void *key, MEM_REC *rec)
+{
+       char *data;
+
+       if (*rec->comment == '\0' &&
+           (strcmp(rec->file, "ig_strdup_printf") == 0 ||
+            strcmp(rec->file, "ig_strdup_vprintf") == 0 ||
+            strcmp(rec->file, "ig_strconcat") == 0 ||
+            strcmp(rec->file, "ig_string_free (free = FALSE)") == 0))
+               data = (char *) rec->p + BUFFER_CHECK_SIZE;
+       else
+               data = rec->comment;
+       fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data);
+}
+
+void ig_mem_profile(void)
+{
+    g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL);
+    g_hash_table_destroy(data);
+    g_hash_table_destroy(preallocs);
+}
+
+static MEM_REC *largest[10];
+
+void ig_profile_largest(void *key, MEM_REC *rec)
+{
+    int n;
+
+    for (n = 0; n < 10; n++)
+    {
+       if (largest[n] == NULL || rec->size > largest[n]->size)
+       {
+           g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n));
+           largest[n] = rec;
+       }
+    }
+}
+
+void ig_mem_profile_largest(void)
+{
+    /*int n;*/
+
+    memset(&largest, 0, sizeof(MEM_REC*)*10);
+    /*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL);
+
+    for (n = 0; n < 10 && largest[n] != NULL; n++)
+    {
+       ig_profile_line(NULL, largest[n]);
+    }*/
+}
+
+void ig_set_data(const char *data)
+{
+    comment = data;
+}
diff --git a/apps/irssi/src/core/memdebug.h b/apps/irssi/src/core/memdebug.h
new file mode 100644 (file)
index 0000000..3f32880
--- /dev/null
@@ -0,0 +1,38 @@
+#ifdef MEM_DEBUG
+void ig_mem_profile(void);
+
+void ig_set_data(const char *data);
+
+void *ig_malloc(int size, const char *file, int line);
+void *ig_malloc0(int size, const char *file, int line);
+void *ig_realloc(void *mem, unsigned long size, const char *file, int line);
+char *ig_strdup(const char *str, const char *file, int line);
+char *ig_strndup(const char *str, int count, const char *file, int line);
+char *ig_strconcat(const char *file, int line, const char *str, ...);
+char *ig_strdup_printf(const char *file, int line, const char *format, ...) G_GNUC_PRINTF (3, 4);
+char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args);
+void ig_free(void *p);
+GString *ig_string_new(const char *file, int line, const char *str);
+void ig_string_free(const char *file, int line, GString *str, int freeit);
+char *ig_strjoinv(const char *file, int line, const char *sepa, char **array);
+char *ig_dirname(const char *file, int line, const char *fname);
+char *ig_module_build_path(const char *file, int line, const char *dir, const char *module);
+
+#define g_malloc(a) ig_malloc(a, __FILE__, __LINE__)
+#define g_malloc0(a) ig_malloc0(a, __FILE__, __LINE__)
+#define g_free ig_free
+#define g_realloc(a,b) ig_realloc(a, b, __FILE__, __LINE__)
+#define g_strdup(a) ig_strdup(a, __FILE__, __LINE__)
+#define g_strndup(a, b) ig_strndup(a, b, __FILE__, __LINE__)
+#define g_string_new(a) ig_string_new(__FILE__, __LINE__, a)
+#define g_string_free(a, b) ig_string_free(__FILE__, __LINE__, a, b)
+#define g_strjoinv(a,b) ig_strjoinv(__FILE__, __LINE__, a, b)
+#define g_dirname(a) ig_dirname(__FILE__, __LINE__, a)
+#define g_module_build_path(a, b) ig_module_build_path(__FILE__, __LINE__, a, b)
+
+#ifndef __STRICT_ANSI__
+#define g_strconcat(a...) ig_strconcat(__FILE__, __LINE__, ##a)
+#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
+#define g_strdup_vprintf(a, b...) ig_strdup_vprintf(__FILE__, __LINE__, a, ##b)
+#endif
+#endif
diff --git a/apps/irssi/src/core/misc.c b/apps/irssi/src/core/misc.c
new file mode 100644 (file)
index 0000000..1af327a
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ 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 (isspace((gint) *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 (toupper(data[pos]) == toupper(key[pos]))
+                       pos++;
+               else {
+                       data++;
+                        pos = 0;
+               }
+       }
+
+       return NULL;
+}
+
+#define isbound(c) \
+       ((unsigned char) (c) < 128 && \
+       (isspace((int) (c)) || ispunct((int) (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 ? (toupper(data[pos]) == 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) + 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 != '?' && toupper(*mask) != 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 (!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;
+}
diff --git a/apps/irssi/src/core/misc.h b/apps/irssi/src/core/misc.h
new file mode 100644 (file)
index 0000000..45c8ce1
--- /dev/null
@@ -0,0 +1,104 @@
+#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);
+
+#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.c b/apps/irssi/src/core/modules.c
new file mode 100644 (file)
index 0000000..9797a05
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ modules.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 "signals.h"
+
+#include "commands.h"
+#include "settings.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);
+       }
+}
+
+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;
+}
+
+#ifdef HAVE_GMODULE
+static char *module_get_name(const char *path, int *start, int *end)
+{
+       const char *name;
+       char *module_name, *ptr;
+
+        name = NULL;
+       if (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) (module_name-ptr));
+
+       return module_name;
+}
+
+static GModule *module_open(const char *name)
+{
+       struct stat statbuf;
+       GModule *module;
+       char *path, *str;
+
+       if (g_path_is_absolute(name) ||
+           (*name == '.' && name[1] == G_DIR_SEPARATOR))
+               path = g_strdup(name);
+       else {
+               /* first try from home dir */
+               str = g_strdup_printf("%s/.silc/modules", g_get_home_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);
+                       return module;
+               }
+
+               /* module not found from home dir, try global module dir */
+               g_free(path);
+               path = g_module_build_path(MODULEDIR, name);
+       }
+
+       module = g_module_open(path, (GModuleFlags) 0);
+       g_free(path);
+       return module;
+}
+
+#define module_error(error, module, text) \
+       signal_emit("module error", 3, GINT_TO_POINTER(error), module, text)
+
+static int module_load_name(const char *path, const char *name, int silent)
+{
+       void (*module_init) (void);
+       GModule *gmodule;
+       MODULE_REC *rec;
+       char *initfunc;
+
+       gmodule = module_open(path);
+       if (gmodule == NULL) {
+               if (!silent) {
+                       module_error(MODULE_ERROR_LOAD, name,
+                                    g_module_error());
+               }
+               return FALSE;
+       }
+
+       /* get the module's init() function */
+       initfunc = g_strconcat(name, "_init", NULL);
+       if (!g_module_symbol(gmodule, initfunc, (gpointer *) &module_init)) {
+               if (!silent)
+                       module_error(MODULE_ERROR_INVALID, name, NULL);
+               g_module_close(gmodule);
+               g_free(initfunc);
+               return FALSE;
+       }
+       g_free(initfunc);
+
+       rec = g_new0(MODULE_REC, 1);
+       rec->name = g_strdup(name);
+        rec->gmodule = gmodule;
+       modules = g_slist_append(modules, rec);
+
+       module_init();
+       settings_check_module(name);
+
+       signal_emit("module loaded", 1, rec);
+       return TRUE;
+}
+#endif
+
+/* 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)
+{
+#ifdef HAVE_GMODULE
+        GString *realpath;
+       char *name, *pname;
+       int ret, start, end;
+
+       g_return_val_if_fail(path != NULL, FALSE);
+
+       if (!g_module_supported())
+               return FALSE;
+
+       name = module_get_name(path, &start, &end);
+       if (module_find(name)) {
+               module_error(MODULE_ERROR_ALREADY_LOADED, name, NULL);
+                g_free(name);
+               return FALSE;
+       }
+
+        /* load "module_core" instead of "module" if it exists */
+       realpath = g_string_new(path);
+       g_string_insert(realpath, end, "_core");
+
+        pname = g_strconcat(name, "_core", NULL);
+       ret = module_load_name(realpath->str, pname, TRUE);
+       g_free(pname);
+
+       if (!ret) {
+                /* load "module" - complain if it's not found */
+               ret = module_load_name(path, name, FALSE);
+       } else 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(realpath, start, "_");
+                       g_string_insert(realpath, start, *prefixes);
+
+                        pname = g_strconcat(*prefixes, "_", name, NULL);
+                       module_load_name(realpath->str, pname, TRUE);
+                       g_free(pname);
+
+                        prefixes++;
+               }
+       }
+
+        g_string_free(realpath, TRUE);
+       g_free(name);
+       return ret;
+#else
+        return FALSE;
+#endif
+}
+
+void module_unload(MODULE_REC *module)
+{
+#ifdef HAVE_GMODULE
+       void (*module_deinit) (void);
+       char *deinitfunc;
+
+       g_return_if_fail(module != NULL);
+
+       modules = g_slist_remove(modules, module);
+
+       signal_emit("module unloaded", 1, module);
+
+       /* call the module's deinit() function */
+       deinitfunc = g_strconcat(module->name, "_deinit", NULL);
+       if (g_module_symbol(module->gmodule, deinitfunc,
+                           (gpointer *) &module_deinit))
+               module_deinit();
+       g_free(deinitfunc);
+
+        settings_remove_module(module->name);
+       commands_remove_module(module->name);
+       signals_remove_module(module->name);
+
+       g_module_close(module->gmodule);
+       g_free(module->name);
+       g_free(module);
+#endif
+}
+
+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..7a83819
--- /dev/null
@@ -0,0 +1,64 @@
+#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(rec) \
+       g_hash_table_lookup((rec)->module_data, MODULE_NAME)
+
+enum {
+       MODULE_ERROR_ALREADY_LOADED,
+       MODULE_ERROR_LOAD,
+       MODULE_ERROR_INVALID
+};
+
+typedef struct {
+       char *name;
+#ifdef HAVE_GMODULE
+       GModule *gmodule;
+#endif
+} MODULE_REC;
+
+extern GSList *modules;
+
+MODULE_REC *module_find(const char *name);
+
+/* 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);
+void module_unload(MODULE_REC *module);
+
+#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..b0e9535
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ 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);
+       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..04eab80
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ 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 successful */
+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;
+}
+
+/* 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..bb6d8e0
--- /dev/null
@@ -0,0 +1,23 @@
+#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);
+
+/* 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..4fc06c0
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ 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, ignore if bind() fails */
+       if (my_ip != NULL) {
+               sin_set_ip(&so, my_ip);
+               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) {
+               /* 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, *origai;
+       char hbuf[NI_MAXHOST];
+       int host_error, 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 */
+       host_error = getaddrinfo(addr, NULL, &hints, &ai);
+       if (host_error != 0)
+               return host_error;
+
+       if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf,
+                       sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
+               return 1;
+
+        origai = ai; count = 0;
+       while (ai != NULL && count < 2) {
+               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++;
+               }
+                ai = ai->ai_next;
+       }
+       freeaddrinfo(origai);
+#else
+       hp = gethostbyname(addr);
+       if (hp == NULL) return h_errno;
+
+       ip4->family = AF_INET;
+       memcpy(&ip4->ip, hp->h_addr, 4);
+#endif
+
+       return 0;
+}
+
+/* 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 != '.' && !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..7dff6f3
--- /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..f9539ff
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ 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) \
+        (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);
+
+       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)
+{
+       if (p1 == NULL) return -1;
+       if (p2 == NULL) return 1;
+
+       if (p1->op && !p2->op) return -1;
+       if (!p1->op && p2->op) return 1;
+
+       if (!p1->op) {
+               if (p1->voice && !p2->voice) return -1;
+               if (!p1->voice && p2->voice) 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 (toupper(*nick) == toupper(*msg)) {
+                               /* total match */
+                               msg++;
+                       } else if (isalnum(*msg) && !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' && !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..f54aa34
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ 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));
+}
+
+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..3f6b84c
--- /dev/null
@@ -0,0 +1,12 @@
+#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);
+
+#endif
diff --git a/apps/irssi/src/core/queries.c b/apps/irssi/src/core/queries.c
new file mode 100644 (file)
index 0000000..d1c5135
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ 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");
+       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);
+       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..4e47040
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ 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 "misc.h"
+#include "write-buffer.h"
+#include "settings.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"));
+}
+
+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);
+}
+
+void rawlog_deinit(void)
+{
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
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..f1b3d07
--- /dev/null
@@ -0,0 +1,26 @@
+/* SERVER_CONNECT_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("SERVER CONNECT", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+/* if we're connecting via proxy, or just NULLs */
+char *proxy;
+int proxy_port;
+char *proxy_string, *proxy_password;
+
+unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
+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 */
+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..7cdc66e
--- /dev/null
@@ -0,0 +1,72 @@
+/* SERVER_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("SERVER", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+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 connection_lost:1; /* Connection lost unintentionally */
+
+NET_SENDBUF_REC *handle;
+int readtag; /* input tag */
+
+/* for net_connect_nonblock() */
+GIOChannel *connect_pipe[2];
+int connect_tag;
+int connect_pid;
+
+/* For deciding if event should be handled internally */
+GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */
+GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */
+GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */
+
+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 */
+
+time_t 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)(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);
+
+/* -- 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..4352bef
--- /dev/null
@@ -0,0 +1,21 @@
+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 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..a9491f9
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ 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->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->conn = conn;
+       rec->next_connect = next_connect;
+
+       reconnects = g_slist_append(reconnects, rec);
+}
+
+void server_reconnect_destroy(RECONNECT_REC *rec, int free_conn)
+{
+       g_return_if_fail(rec != NULL);
+
+       reconnects = g_slist_remove(reconnects, rec);
+
+       signal_emit("server reconnect remove", 1, rec);
+       if (free_conn) server_connect_free(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_reconnect_destroy(rec, FALSE);
+                       CHAT_PROTOCOL(conn)->server_connect(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);
+       if (rec->last_connect > time(NULL)-reconnect_time) {
+               /* can't reconnect this fast, wait.. */
+               server_reconnect_add(conn, rec->last_connect+reconnect_time);
+       } else {
+               /* connect to server.. */
+               CHAT_PROTOCOL(conn)->server_connect(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);
+
+       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_password = g_strdup(src->proxy_password);
+
+       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)->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);
+
+       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);
+
+               if (server->connect_time != 0 &&
+                   time(NULL)-server->connect_time > reconnect_time) {
+                       /* there's been enough time since last connection,
+                          reconnect back immediately */
+                       CHAT_PROTOCOL(conn)->server_connect(conn);
+               } else {
+                       /* reconnect later.. */
+                       server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
+                                                   server->connect_time) + reconnect_time);
+               }
+               return;
+       }
+
+       /* always try to first connect to the first on the list where we
+          haven't got unsuccessful connection attempts for the last 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_free(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, TRUE);
+}
+
+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_reconnect_destroy(rec, FALSE);
+       }
+
+
+       while (list != NULL) {
+               conn = list->data;
+
+               CHAT_PROTOCOL(conn)->server_connect(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);
+                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_reconnect_destroy(rec, FALSE);
+       CHAT_PROTOCOL(conn)->server_connect(conn);
+}
+
+static void cmd_disconnect(const char *data, SERVER_REC *server)
+{
+       RECONNECT_REC *rec;
+       int tag;
+
+       if (g_strncasecmp(data, "RECON-", 6) != 0)
+               return; /* handle only reconnection removing */
+
+       rec = sscanf(data+6, "%d", &tag) == 1 && tag > 0 ?
+               reconnect_find_tag(tag) : NULL;
+
+       if (rec == NULL)
+               signal_emit("server reconnect not found", 1, data);
+       else
+               server_reconnect_destroy(rec, TRUE);
+       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, TRUE);
+       }
+}
+
+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..b51486f
--- /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, int free_conn);
+
+void servers_reconnect_init(void);
+void servers_reconnect_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/servers-redirect.c b/apps/irssi/src/core/servers-redirect.c
new file mode 100644 (file)
index 0000000..ca340fc
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ server-redirect.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 "servers-redirect.h"
+
+static int redirect_group;
+
+static void server_eventtable_destroy(char *key, GSList *value)
+{
+       GSList *tmp;
+
+       g_free(key);
+
+       for (tmp = value; tmp != NULL; tmp = tmp->next) {
+               REDIRECT_REC *rec = tmp->data;
+
+               g_free_not_null(rec->arg);
+               g_free(rec->name);
+               g_free(rec);
+       }
+       g_slist_free(value);
+}
+
+static void server_eventgrouptable_destroy(gpointer key, GSList *value)
+{
+       g_slist_foreach(value, (GFunc) g_free, NULL);
+       g_slist_free(value);
+}
+
+static void server_cmdtable_destroy(char *key, REDIRECT_CMD_REC *value)
+{
+       g_free(key);
+
+       g_slist_foreach(value->events, (GFunc) g_free, NULL);
+       g_slist_free(value->events);
+       g_free(value);
+}
+
+static void sig_disconnected(SERVER_REC *server)
+{
+       g_return_if_fail(IS_SERVER(server));
+
+       if (server->eventtable != NULL) {
+               g_hash_table_foreach(server->eventtable,
+                                    (GHFunc) server_eventtable_destroy, NULL);
+               g_hash_table_destroy(server->eventtable);
+       }
+
+       g_hash_table_foreach(server->eventgrouptable,
+                            (GHFunc) server_eventgrouptable_destroy, NULL);
+       g_hash_table_destroy(server->eventgrouptable);
+
+       if (server->cmdtable != NULL) {
+               g_hash_table_foreach(server->cmdtable,
+                                    (GHFunc) server_cmdtable_destroy, NULL);
+               g_hash_table_destroy(server->cmdtable);
+       }
+}
+
+void server_redirect_initv(SERVER_REC *server, const char *command,
+                          int last, GSList *list)
+{
+       REDIRECT_CMD_REC *rec;
+
+       g_return_if_fail(IS_SERVER(server));
+       g_return_if_fail(command != NULL);
+       g_return_if_fail(last > 0);
+
+       if (g_hash_table_lookup(server->cmdtable, command) != NULL) {
+               /* already in hash table. list of events SHOULD be the same. */
+               g_slist_foreach(list, (GFunc) g_free, NULL);
+               g_slist_free(list);
+               return;
+       }
+
+       rec = g_new(REDIRECT_CMD_REC, 1);
+       rec->last = last;
+       rec->events = list;
+       g_hash_table_insert(server->cmdtable, g_strdup(command), rec);
+}
+
+void server_redirect_init(SERVER_REC *server, const char *command,
+                         int last, ...)
+{
+       va_list args;
+       GSList *list;
+       char *event;
+
+       va_start(args, last);
+       list = NULL;
+       while ((event = va_arg(args, gchar *)) != NULL)
+               list = g_slist_append(list, g_strdup(event));
+       va_end(args);
+
+       server_redirect_initv(server, command, last, list);
+}
+
+int server_redirect_single_event(SERVER_REC *server, const char *arg,
+                                int last, int group, const char *event,
+                                const char *signal, int argpos)
+{
+       REDIRECT_REC *rec;
+       GSList *list, *grouplist;
+       char *origkey;
+
+       g_return_val_if_fail(IS_SERVER(server), 0);
+       g_return_val_if_fail(event != NULL, 0);
+       g_return_val_if_fail(signal != NULL, 0);
+       g_return_val_if_fail(arg != NULL || argpos == -1, 0);
+
+       if (group == 0) group = ++redirect_group;
+
+       rec = g_new0(REDIRECT_REC, 1);
+       rec->arg = arg == NULL ? NULL : g_strdup(arg);
+       rec->argpos = argpos;
+       rec->name = g_strdup(signal);
+       rec->group = group;
+       rec->last = last;
+
+       if (g_hash_table_lookup_extended(server->eventtable, event,
+                                        (gpointer *) &origkey,
+                                        (gpointer *) &list)) {
+               g_hash_table_remove(server->eventtable, origkey);
+       } else {
+               list = NULL;
+               origkey = g_strdup(event);
+       }
+
+       grouplist = g_hash_table_lookup(server->eventgrouptable,
+                                       GINT_TO_POINTER(group));
+       if (grouplist != NULL) {
+               g_hash_table_remove(server->eventgrouptable,
+                                   GINT_TO_POINTER(group));
+       }
+
+       list = g_slist_append(list, rec);
+       grouplist = g_slist_append(grouplist, g_strdup(event));
+
+       g_hash_table_insert(server->eventtable, origkey, list);
+       g_hash_table_insert(server->eventgrouptable,
+                           GINT_TO_POINTER(group), grouplist);
+
+       return group;
+}
+
+void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...)
+{
+       va_list args;
+       char *event, *signal;
+       int argpos, group;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       va_start(args, last);
+
+       group = 0;
+       while ((event = va_arg(args, gchar *)) != NULL) {
+               signal = va_arg(args, gchar *);
+               argpos = va_arg(args, gint);
+
+               group = server_redirect_single_event(server, arg, last > 0,
+                                                    group, event, signal,
+                                                    argpos);
+               last--;
+       }
+
+       va_end(args);
+}
+
+void server_redirect_default(SERVER_REC *server, const char *command)
+{
+       REDIRECT_CMD_REC *cmdrec;
+       REDIRECT_REC *rec;
+       GSList *events, *list, *grouplist;
+       char *event, *origkey;
+       int last;
+
+       g_return_if_fail(IS_SERVER(server));
+       g_return_if_fail(command != NULL);
+
+       if (server->cmdtable == NULL)
+               return; /* not connected yet */
+
+       cmdrec = g_hash_table_lookup(server->cmdtable, command);
+       if (cmdrec == NULL) return;
+
+       /* add all events used by command to eventtable and eventgrouptable */
+       redirect_group++; grouplist = NULL; last = cmdrec->last;
+       for (events = cmdrec->events; events != NULL; events = events->next) {
+               event = events->data;
+
+               if (g_hash_table_lookup_extended(server->eventtable, event,
+                                                (gpointer *) &origkey,
+                                                (gpointer *) &list)) {
+                       g_hash_table_remove(server->eventtable, origkey);
+               } else {
+                       list = NULL;
+                       origkey = g_strdup(event);
+               }
+
+               rec = g_new0(REDIRECT_REC, 1);
+               rec->argpos = -1;
+               rec->name = g_strdup(event);
+               rec->group = redirect_group;
+               rec->last = last > 0;
+
+               grouplist = g_slist_append(grouplist, g_strdup(event));
+               list = g_slist_append(list, rec);
+               g_hash_table_insert(server->eventtable, origkey, list);
+
+               last--;
+       }
+
+       g_hash_table_insert(server->eventgrouptable,
+                           GINT_TO_POINTER(redirect_group), grouplist);
+}
+
+void server_redirect_remove_next(SERVER_REC *server, const char *event,
+                                GSList *item)
+{
+       REDIRECT_REC *rec;
+       GSList *grouplist, *list, *events, *tmp;
+       char *origkey;
+       int group;
+
+       g_return_if_fail(IS_SERVER(server));
+       g_return_if_fail(event != NULL);
+
+       if (!g_hash_table_lookup_extended(server->eventtable, event,
+                                         (gpointer *) &origkey,
+                                         (gpointer *) &list))
+               return;
+
+       rec = item == NULL ? list->data : item->data;
+       if (!rec->last) {
+               /* this wasn't last expected event */
+               return;
+       }
+       group = rec->group;
+
+       /* get list of events from this group */
+       grouplist = g_hash_table_lookup(server->eventgrouptable,
+                                       GINT_TO_POINTER(group));
+
+       /* remove all of them */
+       for (list = grouplist; list != NULL; list = list->next) {
+               char *event = list->data;
+
+               if (!g_hash_table_lookup_extended(server->eventtable, event,
+                                                 (gpointer *) &origkey,
+                                                 (gpointer *) &events)) {
+                       g_warning("server_redirect_remove_next() : "
+                                 "event in eventgrouptable but not in "
+                                 "eventtable");
+                       continue;
+               }
+
+               /* remove the right group */
+               for (tmp = events; tmp != NULL; tmp = tmp->next) {
+                       rec = tmp->data;
+
+                       if (rec->group == group)
+                               break;
+               }
+
+               if (rec == NULL) {
+                       g_warning("server_redirect_remove_next() : "
+                                 "event in eventgrouptable but not in "
+                                 "eventtable (group)");
+                       continue;
+               }
+
+               g_free(event);
+
+               events = g_slist_remove(events, rec);
+               g_free_not_null(rec->arg);
+               g_free(rec->name);
+               g_free(rec);
+
+               /* update hash table */
+               g_hash_table_remove(server->eventtable, origkey);
+               if (events == NULL)
+                       g_free(origkey);
+               else {
+                       g_hash_table_insert(server->eventtable,
+                                           origkey, events);
+               }
+       }
+
+       g_hash_table_remove(server->eventgrouptable, GINT_TO_POINTER(group));
+       g_slist_free(grouplist);
+}
+
+GSList *server_redirect_getqueue(SERVER_REC *server, const char *event,
+                                const char *args)
+{
+       REDIRECT_REC *rec;
+       GSList *list;
+       char **arglist;
+       int found;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+       g_return_val_if_fail(event != NULL, NULL);
+
+       list = g_hash_table_lookup(server->eventtable, event);
+
+       for (; list != NULL; list = list->next) {
+               rec = list->data;
+               if (rec->argpos == -1)
+                       break;
+
+               if (rec->arg == NULL || args == NULL)
+                       continue;
+
+               /* we need to check that the argument is right.. */
+               arglist = g_strsplit(args, " ", -1);
+               found = (strarray_length(arglist) > rec->argpos &&
+                        find_substr(rec->arg, arglist[rec->argpos]));
+               g_strfreev(arglist);
+
+               if (found) break;
+       }
+
+       return list;
+}
+
+void servers_redirect_init(void)
+{
+       redirect_group = 0;
+
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+}
+
+void servers_redirect_deinit(void)
+{
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+}
diff --git a/apps/irssi/src/core/servers-redirect.h b/apps/irssi/src/core/servers-redirect.h
new file mode 100644 (file)
index 0000000..c9e45ce
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __SERVERS_REDIRECT_H
+#define __SERVERS_REDIRECT_H
+
+typedef struct {
+       int last; /* number of "last" events at the start of the events list */
+       GSList *events; /* char* list of events */
+} REDIRECT_CMD_REC;
+
+typedef struct {
+       char *name; /* event name */
+
+       char *arg; /* argument for event we are expecting or NULL */
+       int argpos; /* argument position */
+
+       int group; /* group of events this belongs to */
+       int last; /* if this event is received, remove all the events in this group */
+}
+REDIRECT_REC;
+
+void server_redirect_init(SERVER_REC *server, const char *command, int last, ...);
+void server_redirect_initv(SERVER_REC *server, const char *command, int last, GSList *list);
+/* ... = char *event1, char *event2, ..., NULL */
+
+void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...);
+/* ... = char *event, char *callback_signal, int argpos, ..., NULL */
+
+int server_redirect_single_event(SERVER_REC *server, const char *arg, int last, int group,
+                                const char *event, const char *signal, int argpos);
+void server_redirect_default(SERVER_REC *server, const char *command);
+void server_redirect_remove_next(SERVER_REC *server, const char *event, GSList *item);
+GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, const char *args);
+
+void servers_redirect_init(void);
+void servers_redirect_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..deaf3cc
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ 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_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));
+       }
+}
+
+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->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) {
+               g_free(conn->nick);
+               conn->nick = g_strdup(chatnet->nick);;
+       }
+       if (chatnet->username) {
+                g_free(conn->username);
+               conn->username = g_strdup(chatnet->username);;
+       }
+       if (chatnet->realname) {
+                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);
+       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();
+       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);
+       }
+
+       signal_emit("server setup fill connect", 1, conn);
+       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;
+
+       g_return_val_if_fail(dest != NULL, NULL);
+
+       if (chatnet_find(dest) != NULL) {
+               rec = create_chatnet_conn(dest, port, password, nick);
+               if (rec != NULL)
+                       return rec;
+       }
+
+       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)
+{
+       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) {
+                       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);
+       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);
+       if (chatnet == NULL) /* FIXME: remove this after .98... */ {
+               chatnet = config_node_get_str(node, "ircnet", NULL);
+               if (chatnet != NULL) {
+                        iconfig_node_set_str(node, "chatnet", chatnet);
+                        iconfig_node_set_str(node, "ircnet", 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->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);
+
+       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) {
+               for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+                       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_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..d0807d1
--- /dev/null
@@ -0,0 +1,46 @@
+#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);
+/* 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..f74e7f5
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ 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-redirect.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);
+       if (server->handle != NULL)
+               net_sendbuffer_destroy(server->handle, TRUE);
+
+       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]);
+       }
+
+       MODULE_DATA_DEINIT(server);
+       server_connect_free(server->connrec);
+       g_free_not_null(server->nick);
+       g_free(server->tag);
+       g_free(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);
+
+       /* 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);
+       server->rawlog = rawlog_create();
+
+       server->eventtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
+       server->eventgrouptable = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+       server->cmdtable = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
+
+       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);
+
+       if (ip != NULL)
+               signal_emit("server connecting", 2, server, ip);
+
+       handle = ip == NULL ? NULL : net_connect_ip(ip, port, own_ip);
+       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;
+       }
+
+       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->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);
+
+       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->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);
+
+       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);
+
+        MODULE_DATA_DEINIT(server);
+       server_connect_free(server->connrec);
+       rawlog_destroy(server->rawlog);
+       line_split_free(server->buffer);
+       g_free_not_null(server->version);
+       g_free_not_null(server->away_reason);
+       g_free(server->nick);
+       g_free(server->tag);
+       g_free(server);
+}
+
+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_free(SERVER_CONNECT_REC *conn)
+{
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+
+       signal_emit("server connect free", 1, conn);
+        g_free_not_null(conn->proxy);
+       g_free_not_null(conn->proxy_string);
+       g_free_not_null(conn->proxy_password);
+
+       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);
+        g_free(conn);
+}
+
+void server_change_nick(SERVER_REC *server, const char *nick)
+{
+       g_free(server->connrec->nick);
+       g_free(server->nick);
+       server->connrec->nick = g_strdup(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_redirect_init();
+       servers_setup_init();
+}
+
+void servers_deinit(void)
+{
+       signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
+
+       servers_setup_deinit();
+       servers_redirect_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..75e4cbf
--- /dev/null
@@ -0,0 +1,66 @@
+#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)
+
+/* 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"
+};
+
+extern GSList *servers, *lookup_servers;
+
+void servers_init(void);
+void servers_deinit(void);
+
+/* Disconnect from server */
+void server_disconnect(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_free(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/settings.c b/apps/irssi/src/core/settings.c
new file mode 100644 (file)
index 0000000..10ecba9
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ 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 char *last_config_error_msg;
+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 (last_config_error_msg != NULL) {
+               signal_emit("gui dialog", 2, "error", last_config_error_msg);
+               g_free_and_null(last_config_error_msg);
+       }
+
+       if (config_changed) {
+               /* some backwards compatibility changes were made to
+                  config file, reload it */
+               signal_emit("setup changed", 0);
+       }
+}
+
+/* FIXME: remove after 0.7.98 - only for backward compatibility */
+static void settings_move(SETTINGS_REC *rec, char *value)
+{
+       CONFIG_NODE *setnode, *node;
+
+       setnode = iconfig_node_traverse("settings", TRUE);
+       node = config_node_section(setnode, rec->module, NODE_TYPE_BLOCK);
+
+       iconfig_node_set_str(node, rec->key, value);
+       iconfig_node_set_str(setnode, rec->key, NULL);
+
+        config_changed = TRUE;
+}
+
+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 = node->value; tmp != NULL; tmp = next) {
+               CONFIG_NODE *subnode = tmp->data;
+                next = tmp->next;
+
+               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, *next;
+        int count;
+
+        g_return_if_fail(module != NULL);
+
+       node = iconfig_node_traverse("settings", FALSE);
+       if (node != NULL) {
+               /* FIXME: remove after 0.7.98 */
+               for (tmp = node->value; tmp != NULL; tmp = next) {
+                       CONFIG_NODE *node = tmp->data;
+
+                        next = tmp->next;
+                       if (node->type != NODE_TYPE_KEY)
+                               continue;
+                       set = g_hash_table_lookup(settings, node->key);
+                        if (set != NULL)
+                               settings_move(set, node->value);
+               }
+       }
+       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;
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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 *real_fname;
+
+       real_fname = fname != NULL ? g_strdup(fname) :
+               g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc"
+                               G_DIR_SEPARATOR_S"config", g_get_home_dir());
+
+       if (stat(real_fname, &statbuf) == 0)
+               path = real_fname;
+       else {
+               /* user configuration file not found, use the default one
+                  from sysconfdir */
+                path = SYSCONFDIR"/irssi/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) {
+               last_config_error_msg =
+                       g_strdup_printf("Error opening configuration file %s: %s",
+                                       path, g_strerror(errno));
+               config = config_open(NULL, -1);
+       }
+
+        if (path != NULL)
+               config_parse(config);
+        else
+               config_parse_data(config, default_config, "internal");
+
+       config_change_file_name(config, real_fname, 0660);
+        irssi_config_save_state(real_fname);
+       g_free(real_fname);
+       return config;
+}
+
+static void init_configfile(void)
+{
+       struct stat statbuf;
+       char *str;
+
+       str = g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc", g_get_home_dir());
+       if (stat(str, &statbuf) != 0) {
+               /* ~/.irssi not found, create it. */
+               if (mkpath(str, 0700) != 0) {
+                       g_error("Couldn't create %s directory", str);
+               }
+       } else if (!S_ISDIR(statbuf.st_mode)) {
+               g_error("%s is not a directory.\n"
+                       "You should remove it with command: rm ~/.irssi", str);
+       }
+       g_free(str);
+
+       mainconfig = parse_configfile(NULL);
+       config_last_modifycounter = mainconfig->modifycounter;
+
+       /* any errors? */
+       if (config_last_error(mainconfig) != NULL) {
+               last_config_error_msg =
+                       g_strdup_printf("Ignored errors in configuration "
+                                       "file:\n%s",
+                                       config_last_error(mainconfig));
+       }
+
+       signal(SIGTERM, sig_term);
+}
+
+int settings_reread(const char *fname)
+{
+       CONFIG_REC *tempconfig;
+       char *str;
+
+       if (fname == NULL) fname = "~/.silc/config";
+
+       str = convert_home(fname);
+       tempconfig = parse_configfile(str);
+       g_free(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", 0);
+        return TRUE;
+}
+
+int settings_save(const char *fname)
+{
+       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);
+       }
+        return !error;
+}
+
+static void sig_autosave(void)
+{
+       char *fname, *str;
+
+       if (!settings_get_bool("settings_autosave") ||
+           config_last_modifycounter == mainconfig->modifycounter)
+               return;
+
+       if (!irssi_config_is_changed(NULL))
+               settings_save(NULL);
+       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);
+               g_free(fname);
+       }
+}
+
+void settings_init(void)
+{
+       settings = g_hash_table_new((GHashFunc) g_str_hash,
+                                   (GCompareFunc) g_str_equal);
+
+       last_errors = NULL;
+       last_config_error_msg = 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..cea8c4d
--- /dev/null
@@ -0,0 +1,89 @@
+#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;
+
+/* 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 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..cb964eb
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ 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 emitting; /* signal is being emitted */
+       int altered; /* some signal functions are marked as NULL */
+       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;
+
+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);
+}
+
+/* Destroy the whole signal */
+static void signal_destroy(int signal_id)
+{
+       SIGNAL_REC *rec;
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec != NULL) {
+               /* remove whole signal from memory */
+               g_hash_table_remove(signals, GINT_TO_POINTER(signal_id));
+               g_free(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_remove_from_list(SIGNAL_REC *rec, int signal_id,
+                                   int list, int index)
+{
+       if (rec->emitting) {
+               g_ptr_array_index(rec->siglist[list], index) = NULL;
+               rec->altered = TRUE;
+       } else {
+               g_ptr_array_remove_index(rec->siglist[list], index);
+               g_ptr_array_remove_index(rec->modulelist[list], index);
+               if (signal_is_emitlist_empty(rec))
+                       signal_destroy(signal_id);
+       }
+}
+
+/* Remove signal from emit lists */
+static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id,
+                                   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, signal_id, 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, signal_id, 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);
+}
+
+/* Remove all NULL functions from signal list */
+static void signal_list_clean(SIGNAL_REC *rec)
+{
+       int n, index;
+
+       for (n = 0; n < SIGNAL_LISTS; n++) {
+               if (rec->siglist[n] == NULL)
+                       continue;
+
+               for (index = rec->siglist[n]->len-1; index >= 0; index--) {
+                       if (g_ptr_array_index(rec->siglist[n], index) == NULL) {
+                               g_ptr_array_remove_index(rec->siglist[n], index);
+                               g_ptr_array_remove_index(rec->modulelist[n], index);
+                       }
+               }
+       }
+}
+
+static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
+{
+        SIGNAL_REC *prev_emitted_signal;
+        SIGNAL_FUNC func;
+       int n, index, stopped, stop_emit_count;
+
+       /* signal_stop_by_name("signal"); signal_emit("signal", ...);
+          fails if we compare rec->stop_emit against 0. */
+       stop_emit_count = rec->stop_emit;
+
+       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);
+
+                       if (func != NULL) {
+                                prev_emitted_signal = current_emitted_signal;
+                               current_emitted_signal = rec;
+#if SIGNAL_MAX_ARGUMENTS != 6
+#  error SIGNAL_MAX_ARGS 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;
+               }
+               if (rec->altered) {
+                       signal_list_clean(rec);
+                       rec->altered = FALSE;
+               }
+       }
+
+       return stopped;
+}
+
+static int signal_emitv_id(int signal_id, int params, va_list va)
+{
+       gconstpointer arglist[SIGNAL_MAX_ARGUMENTS];
+       SIGNAL_REC *rec;
+       int n;
+
+       g_return_val_if_fail(signal_id >= 0, FALSE);
+       g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
+
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++)
+               arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer);
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec != NULL && signal_emit_real(rec, arglist))
+               return TRUE;
+
+       return rec != NULL;
+}
+
+int signal_emit(const char *signal, int params, ...)
+{
+       va_list va;
+       int signal_id, ret;
+
+       /* get arguments */
+       signal_id = signal_get_uniq_id(signal);
+
+       va_start(va, params);
+       ret = signal_emitv_id(signal_id, params, va);
+       va_end(va);
+
+       return ret;
+}
+
+int signal_emit_id(int signal_id, int params, ...)
+{
+       va_list va;
+       int ret;
+
+       /* get arguments */
+       va_start(va, params);
+       ret = signal_emitv_id(signal_id, params, va);
+       va_end(va);
+
+       return ret;
+}
+
+/* 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 signal_id, list;
+
+       signal_id = GPOINTER_TO_INT(signal);
+
+       for (list = 0; list < SIGNAL_LISTS; list++) {
+               if (rec->modulelist[list] == NULL)
+                       continue;
+
+               for (index = 0; index < rec->modulelist[list]->len; index++) {
+                       if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index), module) == 0)
+                               signal_remove_from_list(rec, signal_id, list, index);
+               }
+       }
+}
+
+/* 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_remove_module, (void *) module);
+}
+
+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;
+
+       for (n = 0; n < SIGNAL_LISTS; n++) {
+               if (rec->siglist[n] != NULL) {
+                       g_ptr_array_free(rec->siglist[n], TRUE);
+                       g_ptr_array_free(rec->modulelist[n], TRUE);
+               }
+       }
+
+       g_mem_chunk_free(signals_chunk, rec);
+       current_emitted_signal = NULL;
+}
+
+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..40372f9
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ 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 "misc.h"
+
+#define ALIGN_RIGHT 0x01
+#define ALIGN_CUT   0x02
+#define ALIGN_PAD   0x04
+
+#define isvarchar(c) \
+        (isalnum(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 = strarray_length(arglist);
+
+       if (**cmd == '*') {
+               /* get all arguments */
+       } else if (**cmd == '~') {
+               /* get last argument */
+               arg = max = argcount-1;
+       } else {
+               if (isdigit(**cmd)) {
+                       /* first argument */
+                       arg = max = (**cmd)-'0';
+                       (*cmd)++;
+               }
+
+               if (**cmd == '-') {
+                       /* get more than one argument */
+                       (*cmd)++;
+                       if (!isdigit(**cmd))
+                               max = -1; /* get all the rest */
+                       else {
+                               max = (**cmd)-'0';
+                               (*cmd)++;
+                       }
+               }
+               (*cmd)--;
+       }
+
+       str = g_string_new(NULL);
+       while (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 (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
+               /* argument */
+               *free_ret = TRUE;
+               if (arg_used != NULL) *arg_used = TRUE;
+               return getname ? g_strdup_printf("%c", **cmd) :
+                       get_argument(cmd, arglist);
+       }
+
+       if (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)+1);
+               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 (**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 != ']' && !isdigit(*str)) {
+               if (*str == '!')
+                       *flags &= ~ALIGN_CUT;
+               else if (*str == '-')
+                       *flags |= ALIGN_RIGHT;
+               else if (*str == '.')
+                         *flags &= ~ALIGN_PAD;
+               str++;
+       }
+       if (!isdigit(*str))
+               return FALSE; /* expecting number */
+
+       /* get the alignment size */
+       while (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)
+{
+       while (*text != '\0') {
+               if (*text == '%')
+                        g_string_append_c(str, '%');
+               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;
+
+       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 == '\\'){
+                       switch (*cmd) {
+                       case 't':
+                               g_string_append_c(str, '\t');
+                               break;
+                       case 'n':
+                               g_string_append_c(str, '\n');
+                               break;
+                       default:
+                               g_string_append_c(str, *cmd);
+                       }
+                       code = 0;
+               } else if (code == '$') {
+                       char *ret;
+
+                       ret = parse_special((char **) &cmd, server, item,
+                                           arglist, &need_free, arg_used,
+                                           flags);
+                       if (ret != NULL) {
+                                if ((flags & PARSE_FLAG_ESCAPE_VARS) == 0)
+                                       g_string_append(str, ret);
+                               else
+                                        gstring_append_escaped(str, ret);
+                               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';
+               else if (*str != '\0') {
+                       str++;
+                       continue;
+               }
+
+               ret = parse_special_string(start, server, item,
+                                          data, &arg_used, 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);
+               }
+               signal_emit("send command", 3, ret, server, item);
+
+               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 special_vars_signals_do(const char *text, int funccount,
+                                   SIGNAL_FUNC *funcs, int bind)
+{
+       char *ret;
+        int need_free;
+
+       while (*text != '\0') {
+               if (*text == '\\' && text[1] != '\0') {
+                       text += 2;
+               } else if (*text == '$' && text[1] != '\0') {
+                       text++;
+                       ret = parse_special((char **) &text, NULL, NULL,
+                                           NULL, &need_free, NULL,
+                                           PARSE_FLAG_GETNAME);
+                       if (ret != NULL) {
+                                if (bind)
+                                       expando_bind(ret, funccount, funcs);
+                                else
+                                       expando_unbind(ret, funccount, funcs);
+                               if (need_free) g_free(ret);
+                       }
+
+               }
+                else text++;
+       }
+}
+
+void special_vars_add_signals(const char *text,
+                             int funccount, SIGNAL_FUNC *funcs)
+{
+        special_vars_signals_do(text, funccount, funcs, TRUE);
+}
+
+void special_vars_remove_signals(const char *text,
+                                int funccount, SIGNAL_FUNC *funcs)
+{
+        special_vars_signals_do(text, funccount, funcs, FALSE);
+}
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..af02e12
--- /dev/null
@@ -0,0 +1,33 @@
+#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 % */
+
+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);
+
+#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..5c09a5b
--- /dev/null
@@ -0,0 +1,15 @@
+/* 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;
+
+#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..762fc24
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ 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 void read_settings(void)
+{
+       int msecs;
+
+       if (timeout_tag != -1)
+                g_source_remove(timeout_tag);
+
+       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;
+               timeout_tag = g_timeout_add(msecs,
+                                           (GSourceFunc) write_buffer_flush,
+                                           NULL);
+       }
+}
+
+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/Makefile.am b/apps/irssi/src/fe-common/core/Makefile.am
new file mode 100644 (file)
index 0000000..798c271
--- /dev/null
@@ -0,0 +1,62 @@
+noinst_LIBRARIES = libfe_common_core.a
+
+include $(top_srcdir)/Makefile.defines.in
+
+INCLUDES = \
+       $(GLIB_CFLAGS) \
+       -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \
+       -DHELPDIR=\""$(silc_helpdir)"\" \
+       -DSYSCONFDIR=\""$(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 = \
+       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..f49b6c3
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ autorun.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 "line-split.h"
+#include "special-vars.h"
+
+#include "fe-windows.h"
+
+static void sig_autorun(void)
+{
+       char tmpbuf[1024], *str, *path;
+       LINEBUF_REC *buffer = NULL;
+       int f, ret, recvlen;
+
+       /* open ~/.silc/startup and run all commands in it */
+       path = g_strdup_printf("%s/.silc/startup", g_get_home_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);
+}
+
+void autorun_init(void)
+{
+       signal_add_last("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+}
+
+void autorun_deinit(void)
+{
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+}
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..3cbcbf0
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+ 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-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_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 (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)
+{
+       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(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();
+}
+
+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();
+}
+
+/* expand \n, \t and \\ */
+static char *expand_escapes(const char *line, SERVER_REC *server,
+                           WI_ITEM_REC *item)
+{
+       char *ptr, *ret;
+
+       ret = ptr = g_malloc(strlen(line)+1);
+       for (; *line != '\0'; line++) {
+               if (*line != '\\') {
+                       *ptr++ = *line;
+                       continue;
+               }
+
+               line++;
+               if (*line == '\0') {
+                       *ptr++ = '\\';
+                       break;
+               }
+
+               switch (*line) {
+               case 'n':
+                       /* newline .. we need to send another "send text"
+                          event to handle it (or actually the text before
+                          the newline..) */
+                       *ptr = '\0';
+                       signal_emit("send text", 3, ret, server, item);
+                       ptr = ret;
+                       break;
+               case 't':
+                       *ptr++ = '\t';
+                       break;
+               case '\\':
+                       *ptr++ = '\\';
+                       break;
+               default:
+                       *ptr++ = '\\';
+                       *ptr++ = *line;
+                       break;
+               }
+       }
+
+       *ptr = '\0';
+       return ret;
+}
+
+static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       CHANNEL_REC *channel;
+       GList *comp;
+       char *line, *str, *ptr, comp_char;
+
+       g_return_if_fail(data != NULL);
+       if (item == NULL) return;
+
+       line = settings_get_bool("expand_escapes") ?
+               expand_escapes(data, server, item) : g_strdup(data);
+       comp_char = *completion_char;
+
+       /* check for automatic nick completion */
+        ptr = NULL;
+       comp = NULL;
+       channel = CHANNEL(item);
+
+       if (completion_auto && channel != NULL && comp_char != '\0') {
+               ptr = strchr(line, comp_char);
+               if (ptr != NULL) {
+                       *ptr++ = '\0';
+                       if (nicklist_find(channel, line) == NULL) {
+                               comp = completion_channel_nicks(channel,
+                                                               line, NULL);
+                       }
+               }
+       }
+
+       str = g_strdup_printf(ptr == NULL ? "%s %s" : "%s %s%c%s", item->name,
+                             comp != NULL ? (char *) comp->data : line,
+                             comp_char, ptr);
+       signal_emit("command msg", 3, str, server, item);
+
+       g_free(str);
+       g_free(line);
+
+       if (comp != NULL) {
+               g_list_foreach(comp, (GFunc) g_free, NULL);
+               g_list_free(comp);
+       }
+
+       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");
+}
+
+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 connect", (SIGNAL_FUNC) sig_complete_connect);
+       signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       signal_add("message public", (SIGNAL_FUNC) sig_message_public);
+       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 connect", (SIGNAL_FUNC) sig_complete_connect);
+       signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       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("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..cdf4b5a
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ 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"
+
+/* command history */
+static GList *history, *history_pos;
+static int history_lines, history_over_counter;
+static int window_history;
+
+void command_history_add(WINDOW_REC *window, const char *text)
+{
+       GList **phistory, *link;
+       int *phistory_lines;
+
+       g_return_if_fail(text != NULL);
+
+       if (window_history) {
+               /* window specific command history */
+               phistory = &window->history;
+               phistory_lines = &window->history_lines;
+       } else {
+               /* global command history */
+               phistory = &history;
+               phistory_lines = &history_lines;
+       }
+
+       if (settings_get_int("max_command_history") < 1 || *phistory_lines < settings_get_int("max_command_history"))
+               (*phistory_lines)++;
+       else {
+               link = *phistory;
+               g_free(link->data);
+               *phistory = g_list_remove_link(*phistory, link);
+               g_list_free_1(link);
+       }
+
+       *phistory = g_list_append(*phistory, g_strdup(text));
+}
+
+const char *command_history_prev(WINDOW_REC *window, const char *text)
+{
+       GList *pos, **phistory_pos;
+        int *phistory_over_counter;
+
+       if (window_history) {
+               phistory_pos = &window->history_pos;
+               phistory_over_counter = &window->history_over_counter;
+       } else {
+               phistory_pos = &history_pos;
+               phistory_over_counter = &history_over_counter;
+       }
+
+       pos = *phistory_pos;
+       if (*phistory_pos != NULL) {
+               *phistory_pos = (*phistory_pos)->prev;
+               if (*phistory_pos == NULL)
+                        (*phistory_over_counter)++;
+       } else {
+               *phistory_pos = g_list_last(window_history ?
+                                           window->history : history);
+       }
+
+       if (*text != '\0' &&
+           (pos == NULL || strcmp(pos->data, text) != 0)) {
+               /* save the old entry to history */
+               command_history_add(window, text);
+       }
+
+       return *phistory_pos == NULL ? "" : (*phistory_pos)->data;
+}
+
+const char *command_history_next(WINDOW_REC *window, const char *text)
+{
+       GList *pos, **phistory_pos;
+        int *phistory_over_counter;
+
+       if (window_history) {
+               phistory_pos = &window->history_pos;
+               phistory_over_counter = &window->history_over_counter;
+       } else {
+               phistory_pos = &history_pos;
+               phistory_over_counter = &history_over_counter;
+       }
+
+       pos = *phistory_pos;
+
+       if (pos != NULL)
+               *phistory_pos = (*phistory_pos)->next;
+       else if (*phistory_over_counter > 0) {
+               (*phistory_over_counter)--;
+               *phistory_pos = window_history ? window->history : history;
+       }
+
+       if (*text != '\0' &&
+           (pos == NULL || strcmp(pos->data, text) != 0)) {
+               /* save the old entry to history */
+               command_history_add(window, text);
+       }
+       return *phistory_pos == NULL ? "" : (*phistory_pos)->data;
+}
+
+void command_history_clear_pos(WINDOW_REC *window)
+{
+       window->history_over_counter = 0;
+       window->history_pos = NULL;
+        history_over_counter = 0;
+       history_pos = NULL;
+}
+
+static void sig_window_destroyed(WINDOW_REC *window)
+{
+       g_list_foreach(window->history, (GFunc) g_free, NULL);
+       g_list_free(window->history);
+}
+
+static char *special_history_func(const char *text, void *item, int *free_ret)
+{
+       WINDOW_REC *window;
+       GList *tmp;
+        char *findtext, *ret;
+
+       window = item == NULL ? active_win : window_item_window(item);
+
+       findtext = g_strdup_printf("*%s*", text);
+       ret = NULL;
+
+       tmp = window_history ? window->history : history;
+       for (; 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);
+
+       history_lines = 0; history_over_counter = 0;
+       history = NULL; history_pos = NULL;
+
+       read_settings();
+       signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void command_history_deinit(void)
+{
+       signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       g_list_foreach(history, (GFunc) g_free, NULL);
+       g_list_free(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..9f37f06
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __COMMAND_HISTORY_H
+#define __COMMAND_HISTORY_H
+
+#include "fe-windows.h"
+
+void command_history_init(void);
+void command_history_deinit(void);
+
+void command_history_add(WINDOW_REC *window, const char *text, int prepend);
+
+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);
+
+#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..1642761
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ 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) \
+       (isspace((int) (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)
+{
+       static int startpos = 0, wordlen = 0;
+
+       GString *result;
+       char *word, *wordstart, *linestart, *ret;
+       int want_space;
+
+       g_return_val_if_fail(line != NULL, NULL);
+       g_return_val_if_fail(pos != NULL, NULL);
+
+       if (complist != NULL && *pos == last_line_pos &&
+           strcmp(line, last_line) == 0) {
+               /* 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();
+
+               /* 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 (*pos > 0 && line[*pos-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;
+               }
+
+               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;
+}
+
+GList *list_add_file(GList *list, const char *name)
+{
+       struct stat statbuf;
+       char *fname;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       fname = convert_home(name);
+       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)
+{
+        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);
+       dir = g_dirname(realpath);
+       g_free(realpath);
+
+       /* open directory for reading */
+       dirp = opendir(dir);
+       g_free(dir);
+       if (dirp == NULL) return NULL;
+
+       dir = g_dirname(path);
+       if (*dir == G_DIR_SEPARATOR && dir[1] == '\0')
+               *dir = '\0'; /* completing file in root directory */
+       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 = g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name);
+                       list = list_add_file(list, name);
+                       g_free(name);
+               }
+       }
+       closedir(dirp);
+
+       g_free(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 : node->value;
+
+       len = strlen(alias);
+       complist = NULL;
+       for (; tmp != NULL; tmp = tmp->next) {
+               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 (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);
+
+       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 (strchr(cmdchars, *word) && *linestart == '\0') {
+               /* 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 */
+        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_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);
+       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("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 run", (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 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 run", (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..ef0fe06
--- /dev/null
@@ -0,0 +1,16 @@
+#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 */
+char *word_complete(WINDOW_REC *window, const char *line, int *pos);
+
+GList *filename_complete(const char *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..67557b9
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ 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 "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 signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+       CHANNEL_REC *channel;
+
+       g_return_if_fail(window != NULL);
+
+       channel = CHANNEL(item);
+        if (channel != NULL) channel_destroy(channel);
+}
+
+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
+               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;
+       char *linebuf, nickmode[2] = { 0, 0 };
+       int *columns, cols, rows, last_col_rows, col, row, max_width;
+        int item_extra, linebuf_size;
+
+       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 &&
+           max_width > settings_get_int("names_max_width"))
+               max_width = settings_get_int("names_max_width");
+
+        /* remove width of 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);
+       }
+
+       /* 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(NULL);
+       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;
+
+               nickmode[0] = rec->op ? '@' : rec->voice ? '+' : ' ';
+
+               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));
+
+               format = format_get_text(MODULE_NAME, NULL,
+                                        channel->server, channel->name,
+                                        TXT_NAMES_NICK, 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);
+                       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(linebuf);
+}
+
+void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
+{
+       NICK_REC *nick;
+       GSList *tmp, *nicklist, *sorted;
+       int nicks, normal, voices, ops;
+
+       nicks = normal = voices = 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) {
+                       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 */
+       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, voices, normal);
+}
+
+/* SYNTAX: NAMES [-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 (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 destroy", (SIGNAL_FUNC) signal_window_item_destroy);
+       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", "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 destroy", (SIGNAL_FUNC) signal_window_item_destroy);
+       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..a8aa697
--- /dev/null
@@ -0,0 +1,15 @@
+#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
+
+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..2483690
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ 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 "channels.h"
+#include "servers-setup.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 autorun_init(void);
+void autorun_deinit(void);
+
+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 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));
+}
+
+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));
+}
+
+void fe_common_core_init(void)
+{
+       static struct poptOption options[] = {
+               { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/ircnet", "SERVER" },
+               { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "SERVER" },
+               { "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_bool("lookandfeel", "msgs_timestamps", FALSE);
+       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_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);
+
+       autorun_init();
+       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);
+}
+
+void fe_common_core_deinit(void)
+{
+       autorun_deinit();
+       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));
+       }
+
+       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);
+       }
+
+       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)
+{
+       signal_emit("irssi init read settings", 0);
+
+#ifdef SIGPIPE
+       signal(SIGPIPE, SIG_IGN);
+#endif
+
+       if (cmdline_nick != NULL) {
+               /* override nick found from setup */
+               settings_set_str("nick", cmdline_nick);
+       }
+
+       if (cmdline_hostname != NULL) {
+               /* override host name found from setup */
+               settings_set_str("hostname", cmdline_hostname);
+       }
+
+       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);
+
+       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..7b8f7f9
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ 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 "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_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)
+{
+       g_return_if_fail(data != NULL);
+
+       if (*data == '\0') {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                         "Client: "PACKAGE" " IRSSI_VERSION);
+       }
+}
+
+/* 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 sig_stop(void)
+{
+       signal_stop();
+}
+
+static void event_command(const char *data)
+{
+       const char *cmdchar;
+
+       /* 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 stripped", (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 stripped", (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);
+
+       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);
+
+       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..bc8f469
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ fe-exec.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 "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>
+
+static GSList *processes;
+static int signal_exec_input;
+
+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->name = rec->name != NULL ?
+               g_strdup_printf("%%%s", rec->name) :
+               g_strdup_printf("%%%d", rec->id);
+
+       item->window = window;
+       item->createtime = time(NULL);
+        item->process = rec;
+
+       MODULE_DATA_INIT(item);
+       window_item_add(window, (WI_ITEM_REC *) item, FALSE);
+        return item;
+}
+
+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 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;
+       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"));
+
+       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;
+       int status = GPOINTER_TO_INT(statusp);
+
+        rec = process_find_pid(GPOINTER_TO_INT(pid));
+       if (rec == NULL) return;
+
+       /* process exited */
+       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, MSGLEVEL_CLIENTCRAP,
+                         "%s", text);
+       } else {
+               printtext_window(rec->target_win, MSGLEVEL_CLIENTCRAP,
+                                "%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 sig_window_item_destroyed(WINDOW_REC *window, EXEC_WI_REC *item)
+{
+       if (IS_EXEC_WI(item)) {
+                item->process->target_item = NULL;
+               exec_wi_destroy(item);
+       }
+}
+
+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");
+
+        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("window item destroy", (SIGNAL_FUNC) sig_window_item_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("window item destroy", (SIGNAL_FUNC) sig_window_item_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..7f569ec
--- /dev/null
@@ -0,0 +1,45 @@
+#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;
+
+        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" */
+};
+
+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..7eb145d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ 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)
+{
+        *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)
+{
+       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 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..3523d52
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ 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 ");
+       if (rec->fullword) g_string_sprintfa(options, "-full ");
+       if (rec->replies) g_string_sprintfa(options, "-replies ");
+       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;
+
+       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 &&
+           active_win->active_server->ischannel(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);
+       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 (rec->level == 0) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_UNIGNORED,
+                           rec->mask == NULL ? "*" : rec->mask);
+       }
+
+       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;
+
+       if (*data == '\0')
+                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       if (is_numeric(data, ' ')) {
+               /* with index number */
+               tmp = g_slist_nth(ignores, atoi(data)-1);
+               rec = tmp == NULL ? NULL : tmp->data;
+       } else {
+               /* with mask */
+               const char *chans[2] = { "*", NULL };
+
+               if (active_win->active_server != NULL &&
+                   active_win->active_server->ischannel(data)) {
+                       chans[0] = data;
+                       data = NULL;
+               }
+               rec = ignore_find("*", data, (char **) chans);
+       }
+
+       if (rec != NULL) {
+               rec->level = 0;
+               ignore_update_rec(rec);
+       } else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_IGNORE_NOT_FOUND, data);
+       }
+}
+
+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..60d9632
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ 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 "servers.h"
+#include "levels.h"
+#include "misc.h"
+#include "log.h"
+#include "special-vars.h"
+#include "settings.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)
+
+#define LOG_DIR_CREATE_MODE 0770
+
+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 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>] <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# */
+               ltoa(window, active_win->refnum);
+               log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window,
+                            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;
+
+       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_CLIENTNOTICE, 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_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_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 &&
+                   g_strcasecmp(logitem->servertag, server->tag) == 0)
+                       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 void autolog_open(SERVER_REC *server, const char *target)
+{
+       LOG_REC *log;
+       char *fname, *dir, *fixed_target, *tag;
+
+        tag = server == NULL ? NULL : server->tag;
+       log = logs_find_item(LOG_ITEM_TARGET, target, 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.. */
+       fixed_target = g_strdup(target);
+        replace_chars(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);
+               log_item_add(log, LOG_ITEM_TARGET, target, 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 *target,
+                              int level)
+{
+       char **targets, **tmp;
+
+       if (level == MSGLEVEL_PARTS || /* FIXME: kind of a kludge, but we don't want to reopen logs when we're parting the channel with /WINDOW CLOSE.. */
+           (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, *tmp);
+       g_strfreev(targets);
+}
+
+static void log_single_line(WINDOW_REC *window, void *server,
+                           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, 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, *tmp, level, text, FALSE);
+               g_strfreev(targets);
+       }
+}
+
+static void log_line(WINDOW_REC *window, SERVER_REC *server,
+                    const char *target, int level, const char *text)
+{
+       char **lines, **tmp;
+
+       if (level == MSGLEVEL_NEVER)
+               return;
+
+       /* let autolog open the log records */
+       autolog_open_check(server, target, 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(window, server, target, level, *tmp);
+       g_strfreev(lines);
+}
+
+static void sig_printtext_stripped(TEXT_DEST_REC *dest, const char *text)
+{
+       if (skip_next_printtext) {
+               skip_next_printtext = FALSE;
+               return;
+       }
+
+       log_line(dest->window, dest->server, dest->target,
+                dest->level, text);
+}
+
+static void sig_print_format(THEME_REC *theme, const char *module,
+                            TEXT_DEST_REC *dest, void *formatnum, char **args)
+{
+       char *str, *stripped, *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. */
+               stripped = strip_codes(str);
+               log_line(dest->window, dest->server, dest->target,
+                        dest->level, stripped);
+               g_free(stripped);
+       }
+       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(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_CLIENTNOTICE,
+                   TXT_LOG_LOCKED, log->fname);
+}
+
+static void sig_log_create_failed(LOG_REC *log)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_LOG_CREATE_FAILED, log->fname, g_strerror(errno));
+}
+
+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_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
+       settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
+        settings_add_bool("log", "autolog", FALSE);
+        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 stripped", (SIGNAL_FUNC) sig_printtext_stripped);
+       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("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");
+}
+
+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 stripped", (SIGNAL_FUNC) sig_printtext_stripped);
+       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("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..fa5e88a
--- /dev/null
@@ -0,0 +1,684 @@
+/*
+ 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 "window-items.h"
+#include "fe-queries.h"
+#include "channels.h"
+#include "nicklist.h"
+#include "hilight-text.h"
+#include "ignore.h"
+#include "printtext.h"
+
+#define ishighalnum(c) ((unsigned char) (c) >= 128 || 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 && !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;
+}
+
+char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
+{
+        NICK_REC *nickrec;
+        char *emptystr;
+
+       g_return_val_if_fail(nick != NULL, NULL);
+
+       if (!settings_get_bool("show_nickmode"))
+                return "";
+
+        emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
+
+       nickrec = channel == NULL ? NULL :
+               nicklist_find(channel, nick);
+       return nickrec == NULL ? emptystr :
+               (nickrec->op ? "@" : (nickrec->voice ? "+" : emptystr));
+}
+
+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->voice ? "+" : emptystr));
+}
+
+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 | (for_me || color != NULL ?
+                                  MSGLEVEL_HILIGHT : MSGLEVEL_NOHILIGHT);
+
+       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)
+{
+       if (ignore_check(server, oldnick, address,
+                        channel, newnick, MSGLEVEL_NICKS))
+               return;
+
+       printformat(server, channel, MSGLEVEL_NICKS,
+                   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;
+       }
+
+       for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *query = tmp->data;
+               WINDOW_REC *window =
+                       window_item_window((WI_ITEM_REC *) query);
+
+               if (g_strcasecmp(query->name, oldnick) != 0 ||
+                   g_slist_find(windows, window) != NULL)
+                       continue;
+
+               windows = g_slist_append(windows, window);
+               print_nick_change_channel(server, query->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)
+{
+       print_nick_change(server, newnick, oldnick, address, TRUE);
+}
+
+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);
+
+       signal_add("message public", (SIGNAL_FUNC) sig_message_public);
+       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("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_add("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_add("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_add("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_add("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_add("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_add("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_add("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..e351dc7
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ 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 "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "chat-protocols.h"
+
+#include "printtext.h"
+
+static void sig_module_error(void *number, const char *module,
+                            const char *data)
+{
+       switch (GPOINTER_TO_INT(number)) {
+       case MODULE_ERROR_ALREADY_LOADED:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_ALREADY_LOADED, module);
+               break;
+       case MODULE_ERROR_LOAD:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_LOAD_ERROR, module, data);
+               break;
+       case MODULE_ERROR_INVALID:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_INVALID, module);
+               break;
+       }
+}
+
+static void sig_module_loaded(MODULE_REC *rec)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_MODULE_LOADED, rec->name);
+}
+
+static void sig_module_unloaded(MODULE_REC *rec)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_MODULE_UNLOADED, rec->name);
+}
+
+static void cmd_load_list(void)
+{
+       GSList *tmp;
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_HEADER);
+       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+               MODULE_REC *rec = tmp->data;
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_MODULE_LINE, rec->name);
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_MODULE_FOOTER);
+}
+
+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> */
+static void cmd_load(const char *data)
+{
+#ifdef HAVE_GMODULE
+       char **module_prefixes;
+
+       g_return_if_fail(data != NULL);
+       if (*data == '\0')
+               cmd_load_list();
+       else {
+               module_prefixes = module_prefixes_get();
+               module_load(data, module_prefixes);
+                module_prefixes_free(module_prefixes);
+       }
+#else
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                 "Dynamic modules loading not supported");
+#endif
+}
+
+/* SYNTAX: UNLOAD <module> */
+static void cmd_unload(const char *data)
+{
+       MODULE_REC *rec;
+
+       g_return_if_fail(data != NULL);
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       rec = module_find(data);
+       if (rec != NULL) module_unload(rec);
+}
+
+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);
+}
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..1924464
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ 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 "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)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+       if (window_item_window(query) == NULL) {
+               window_item_create((WI_ITEM_REC *) query,
+                                  GPOINTER_TO_INT(automatic));
+               printformat(query->server, query->name, MSGLEVEL_CLIENTNOTICE,
+                           TXT_QUERY_STARTED, query->name);
+       }
+}
+
+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;
+
+       g_return_if_fail(IS_QUERY(query));
+
+       window = window_item_window((WI_ITEM_REC *) query);
+       if (window != NULL) {
+               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)
+{
+       g_return_if_fail(query != NULL);
+
+       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 signal_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+       QUERY_REC *query;
+
+       g_return_if_fail(window != NULL);
+
+       query = QUERY(item);
+       if (query != NULL) query_destroy(query);
+}
+
+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;
+
+       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 */
+       query_change_server(query, server);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_QUERY_SERVER_CHANGED,
+                   query->name, server->tag);
+       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 (*data == '\0') {
+               /* remove current query */
+               cmd_unquery("", server, item);
+               return;
+       }
+
+       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') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       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)
+               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') {
+               /* FIXME: we'll need some function that does both
+                  of these. and separate the , and . target handling
+                  from own_private messagge.. */
+               server->send_message(server, nick, msg);
+
+               signal_emit("message own_private", 4,
+                           server, msg, nick, nick);
+       }
+
+       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_last("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
+       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("window item destroy", (SIGNAL_FUNC) signal_window_item_destroy);
+       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..f2327c5
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ 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 (*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);
+       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, SERVER_REC *server, void *item)
+{
+       GHashTable *optlist;
+       char *addr;
+       void *free_arg;
+
+       if (*data == '\0') {
+               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();
+               return;
+       }
+
+       if (g_strncasecmp(data, "add ", 4) == 0 ||
+           g_strncasecmp(data, "remove ", 7) == 0 ||
+           g_strcasecmp(data, "list") == 0 ||
+           g_strncasecmp(data, "list ", 5) == 0) {
+               command_runsub("server", data, server, item);
+               signal_stop();
+               return;
+       }
+
+       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(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONNECTION_LOST, server->connrec->address);
+       } else {
+               printformat(NULL, 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(NULL, 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(NULL, 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(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_LAG_DISCONNECTED, server->connrec->address, time(NULL)-server->lag_sent);
+}
+
+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 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 -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 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..0d8aaa9
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ 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 = "";
+       }
+       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s = %s", 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 */
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%_[ %s ]", 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)
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %s", 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)
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %_%s", key);
+       else if (type != SETTING_TYPE_BOOLEAN)
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Setting %_%s%_ isn't boolean, use /SET", key);
+       else {
+               set_boolean(key, *value != '\0' ? value : "TOGGLE");
+                set_print(settings_get_record(key));
+       }
+
+        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 : node->value;
+
+       /* first get the list of aliases sorted */
+       list = NULL;
+       aliaslen = strlen(alias);
+       for (; tmp != NULL; tmp = tmp->next) {
+               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)
+{
+       char *fname;
+
+       fname = *data != '\0' ? g_strdup(data) :
+               g_strdup_printf("%s/.silc/config", g_get_home_dir());
+       if (settings_reread(fname)) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_RELOADED, fname);
+       }
+       g_free(fname);
+}
+
+static void settings_save_fe(const char *fname)
+{
+       if (settings_save(fname)) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_SAVED, fname);
+       }
+}
+
+static void settings_save_confirm(const char *line, char *fname)
+{
+       if (line[0] == 'Y')
+               settings_save_fe(fname);
+       g_free(fname);
+}
+
+/* SYNTAX: SAVE [<file>] */
+static void cmd_save(const char *data)
+{
+       char *format;
+
+       if (*data == '\0')
+               data = mainconfig->fname;
+
+       if (!irssi_config_is_changed(data)) {
+               settings_save_fe(data);
+               return;
+       }
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_CONFIG_MODIFIED, data);
+
+       format = format_get_text(MODULE_NAME, NULL, NULL, NULL,
+                                TXT_OVERWRITE_CONFIG);
+       keyboard_entry_redirect((SIGNAL_FUNC) settings_save_confirm,
+                               format, 0, g_strdup(data));
+        g_free(format);
+}
+
+static void settings_clean_confirm(const char *line)
+{
+       if (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..3c48261
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ 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->level == 0)
+                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_level(WINDOW_REC *window, int level)
+{
+       g_return_if_fail(window != NULL);
+
+       window->level = level;
+        signal_emit("window level 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;
+}
+
+static 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;
+}
+
+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 = 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 sig_print_text(void)
+{
+       GSList *tmp;
+       char month[100];
+       time_t t;
+       struct tm *tm;
+
+       t = time(NULL);
+       tm = localtime(&t);
+       if (strftime(month, sizeof(month), "%b", tm) <= 0)
+               month[0] = '\0';
+
+       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) {
+               printformat_window(tmp->data, MSGLEVEL_NEVER, TXT_DAYCHANGE,
+                                  tm->tm_mday, tm->tm_mon+1,
+                                  1900+tm->tm_year, month);
+       }
+}
+
+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..c9ffe69
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef __WINDOWS_H
+#define __WINDOWS_H
+
+#include "servers.h"
+
+#define STRUCT_SERVER_REC SERVER_REC
+#include "window-item-def.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;
+
+typedef struct {
+       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 sticky_refnum:1;
+       unsigned int destroying:1;
+
+       /* window-specific command line history */
+       GList *history, *history_pos;
+       int history_lines, history_over_counter;
+
+       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;
+} WINDOW_REC;
+
+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_level(WINDOW_REC *window, int level);
+
+/* 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);
+
+GSList *windows_get_sorted(void);
+
+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..b933c06
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+ 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 "fe-windows.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;
+
+static int timestamps, msgs_timestamps;
+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;
+}
+
+int format_expand_styles(GString *out, char format)
+{
+       char *p;
+
+       switch (format) {
+       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 '%':
+               g_string_append_c(out, '%');
+               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;
+       default:
+               /* check if it's a background color */
+               p = strchr(format_backs, format);
+               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 (format == 'p') format = 'm';
+               p = strchr(format_fores, format);
+               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 (format == 'P') format = 'M';
+               p = strchr(format_boldfores, format);
+               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)
+{
+       dest->server = server;
+       dest->target = target;
+       dest->level = level;
+       dest->window = window != NULL ? window :
+               window_find_closest(server, target, level);
+
+       dest->hilight_priority = 0;
+        dest->hilight_color = NULL;
+}
+
+/* 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)) {
+                                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)) {
+                                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)
+{
+       GString *out;
+       char code, *ret;
+
+        g_return_val_if_fail(text != NULL, NULL);
+
+       out = g_string_new(NULL);
+
+       code = 0;
+       while (*text != '\0') {
+               if (code == '%') {
+                       /* color code */
+                       if (!format_expand_styles(out, *text)) {
+                               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)) {
+                               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,
+                                           active_win == NULL ? NULL :
+                                           active_win->active_server,
+                                           active_win == NULL ? NULL :
+                                           active_win->active, 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 = dest->window->theme == NULL ? current_theme :
+                       dest->window->theme;
+       }
+
+       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 = dest.window->theme == NULL ? current_theme :
+               dest.window->theme;
+
+       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;
+
+       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);
+}
+
+#define show_timestamp(level) \
+       ((level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) == 0 && \
+       (timestamps || (msgs_timestamps && ((level) & MSGLEVEL_MSGS))))
+
+static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
+{
+       struct tm *tm;
+       int diff;
+
+       if (!show_timestamp(dest->level))
+               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);
+       return format_get_text_theme(theme, MODULE_NAME, dest, TXT_TIMESTAMP,
+                                    tm->tm_year+1900,
+                                    tm->tm_mon+1, tm->tm_mday,
+                                    tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
+{
+       SERVER_REC *server;
+       int count = 0;
+
+       server = dest->server;
+
+       if (server == NULL || hide_server_tags ||
+           (dest->window->active != NULL &&
+            dest->window->active->server == 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++;
+       }
+
+       return count < 2 ? NULL :
+               format_get_text_theme(theme, MODULE_NAME, dest,
+                                     TXT_SERVERTAG, 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(PRINTFLAG_NEWLINE),
+                      "", GINT_TO_POINTER(-1));
+}
+
+/* parse ANSI color string */
+static char *get_ansi_color(THEME_REC *theme, char *str,
+                           int *fg_ret, int *bg_ret, int *flags_ret)
+{
+       static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       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 (isdigit((int) *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 &= ~PRINTFLAG_INDENT;
+                       break;
+               case 1:
+                       /* hilight */
+                       flags |= PRINTFLAG_BOLD;
+                       break;
+               case 5:
+                       /* blink */
+                       flags |= PRINTFLAG_BLINK;
+                       break;
+               case 7:
+                       /* reverse */
+                       flags |= PRINTFLAG_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 (!isdigit((int) **str) && **str != ',') {
+               fg = -1;
+               bg = -1;
+       } else {
+               /* foreground color */
+               if (**str != ',') {
+                       fg = **str-'0';
+                        (*str)++;
+                       if (isdigit((int) **str)) {
+                               fg = fg*10 + (**str-'0');
+                               (*str)++;
+                       }
+               }
+               if (**str == ',') {
+                       /* background color */
+                       (*str)++;
+                       if (!isdigit((int) **str))
+                               bg = -1;
+                       else {
+                               bg = **str-'0';
+                               (*str)++;
+                               if (isdigit((int) **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 (!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);
+               }
+
+               if (*str != '\0') {
+                        /* 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 &= ~PRINTFLAG_INDENT;
+               }
+
+               if (type == '\n')
+                       format_newline(dest->window);
+
+               if (*ptr == '\0')
+                       break;
+
+               switch (type)
+               {
+               case 2:
+                       /* bold */
+                       if (!hide_text_style)
+                               flags ^= PRINTFLAG_BOLD;
+                       break;
+               case 3:
+                       /* MIRC color */
+                       get_mirc_color((const char **) &ptr,
+                                      hide_text_style ? NULL : &fgcolor,
+                                      hide_text_style ? NULL : &bgcolor);
+                       if (!hide_text_style)
+                               flags |= PRINTFLAG_MIRC_COLOR;
+                       break;
+               case 4:
+                       /* user specific colors */
+                       flags &= ~PRINTFLAG_MIRC_COLOR;
+                       switch (*ptr) {
+                       case FORMAT_STYLE_BLINK:
+                               flags ^= PRINTFLAG_BLINK;
+                               break;
+                       case FORMAT_STYLE_UNDERLINE:
+                               flags ^= PRINTFLAG_UNDERLINE;
+                               break;
+                       case FORMAT_STYLE_BOLD:
+                               flags ^= PRINTFLAG_BOLD;
+                               break;
+                       case FORMAT_STYLE_REVERSE:
+                               flags ^= PRINTFLAG_REVERSE;
+                               break;
+                       case FORMAT_STYLE_INDENT:
+                               flags |= PRINTFLAG_INDENT;
+                               break;
+                       case FORMAT_STYLE_DEFAULTS:
+                               fgcolor = bgcolor = -1;
+                               flags &= PRINTFLAG_INDENT;
+                               break;
+                       default:
+                               if (*ptr != FORMAT_COLOR_NOCHANGE) {
+                                       fgcolor = (unsigned char) *ptr-'0';
+                                       if (fgcolor <= 7)
+                                               flags &= ~PRINTFLAG_BOLD;
+                                       else {
+                                               /* bold */
+                                               if (fgcolor != 8) fgcolor -= 8;
+                                               flags |= PRINTFLAG_BOLD;
+                                       }
+                               }
+                               ptr++;
+                               if (*ptr != FORMAT_COLOR_NOCHANGE)
+                                       bgcolor = *ptr-'0';
+                       }
+                       ptr++;
+                       break;
+               case 6:
+                       /* blink */
+                       if (!hide_text_style)
+                               flags ^= PRINTFLAG_BLINK;
+                       break;
+               case 15:
+                       /* remove all styling */
+                       fgcolor = bgcolor = -1;
+                       flags &= PRINTFLAG_INDENT;
+                       break;
+               case 22:
+                       /* reverse */
+                       if (!hide_text_style)
+                               flags ^= PRINTFLAG_REVERSE;
+                       break;
+               case 31:
+                       /* underline */
+                       if (!hide_text_style)
+                               flags ^= PRINTFLAG_UNDERLINE;
+                       break;
+               case 27:
+                       /* ansi color code */
+                       ptr = 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)
+{
+       hide_server_tags = settings_get_bool("hide_server_tags");
+       hide_text_style = settings_get_bool("hide_text_style");
+       timestamps = settings_get_bool("timestamps");
+       timestamp_timeout = settings_get_int("timestamp_timeout");
+       msgs_timestamps = settings_get_bool("msgs_timestamps");
+}
+
+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..86c53e6
--- /dev/null
@@ -0,0 +1,115 @@
+#ifndef __FORMATS_H
+#define __FORMATS_H
+
+#include "themes.h"
+#include "fe-windows.h"
+
+#define PRINTFLAG_BOLD          0x01
+#define PRINTFLAG_REVERSE       0x02
+#define PRINTFLAG_UNDERLINE     0x04
+#define PRINTFLAG_BLINK         0x08
+#define PRINTFLAG_MIRC_COLOR    0x10
+#define PRINTFLAG_INDENT        0x20
+#define PRINTFLAG_NEWLINE       0x40
+
+#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];
+};
+
+typedef struct {
+       WINDOW_REC *window;
+       SERVER_REC *server;
+       const char *target;
+       int level;
+
+       int hilight_priority;
+       char *hilight_color;
+} TEXT_DEST_REC;
+
+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);
+
+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_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_DEFAULTS  (0x06 + FORMAT_STYLE_SPECIAL)
+int format_expand_styles(GString *out, char format);
+
+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..197957f
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ 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 HILIGHT_REC *next_nick_hilight, *next_line_hilight;
+static int next_hilight_start, next_hilight_end;
+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);
+}
+
+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;
+
+        dest->hilight_color = hilight_get_act_color(rec);
+}
+
+static void sig_print_text_stripped(TEXT_DEST_REC *dest, const char *str)
+{
+        HILIGHT_REC *hilight;
+
+       g_return_if_fail(str != NULL);
+
+       if (next_nick_hilight != NULL) {
+               if (!next_nick_hilight->nick) {
+                        /* non-nick hilight wanted */
+                       hilight = next_nick_hilight;
+                       next_nick_hilight = NULL;
+                       if (!hilight_match_text(hilight, str,
+                                               &next_hilight_start,
+                                               &next_hilight_end)) {
+                                next_hilight_start = 0;
+                                next_hilight_end = strlen(str);
+                       }
+               } else {
+                       /* nick is highlighted, just set priority */
+                       hilight_update_text_dest(dest, next_nick_hilight);
+                       next_nick_hilight = NULL;
+                       return;
+               }
+       } else {
+               if (dest->level & (MSGLEVEL_NOHILIGHT|MSGLEVEL_HILIGHT))
+                       return;
+
+               hilight = hilight_match(dest->server, dest->target,
+                                       NULL, NULL, dest->level, str,
+                                       &next_hilight_start,
+                                       &next_hilight_end);
+       }
+
+       if (hilight != NULL) {
+               /* update the level / hilight info */
+               hilight_update_text_dest(dest, hilight);
+
+               next_line_hilight = hilight;
+       }
+}
+
+static void sig_print_text(TEXT_DEST_REC *dest, const char *str)
+{
+       char *color, *newstr;
+        int next_hilight_len;
+
+       if (next_line_hilight == NULL)
+                return;
+
+       color = hilight_get_color(next_line_hilight);
+       next_hilight_len = next_hilight_end-next_hilight_start;
+
+       if (!next_line_hilight->word) {
+               /* hilight whole line */
+               char *tmp = strip_codes(str);
+               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(str, next_hilight_start, NULL, NULL);
+               g_string_append(tmp, str);
+                g_string_truncate(tmp, pos);
+
+               /* color */
+                g_string_append(tmp, color);
+
+               /* middle of the line, stripped */
+               middle = strip_codes(str+pos);
+                pos = tmp->len;
+               g_string_append(tmp, middle);
+                g_string_truncate(tmp, pos+next_hilight_len);
+                g_free(middle);
+
+               /* end of the line */
+               pos = strip_real_length(str, next_hilight_end,
+                                       &color_pos, &color_len);
+               if (color_pos > 0)
+                       lastcolor = g_strndup(str+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, str+pos);
+               g_free(lastcolor);
+
+                newstr = tmp->str;
+                g_string_free(tmp, FALSE);
+       }
+
+       next_line_hilight = NULL;
+       signal_emit("print text", 2, dest, newstr);
+
+       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);
+
+        next_nick_hilight = 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;
+       }
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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) {
+               if (*colorarg != '\0')
+                       rec->color = g_strdup(colorarg);
+               else
+                       g_free_and_null(rec->color);
+       }
+       if (actcolorarg != NULL) {
+               if (*actcolorarg != '\0')
+                       rec->act_color = g_strdup(actcolorarg);
+               else
+                       g_free_and_null(rec->act_color);
+       }
+
+#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");
+
+       next_nick_hilight = NULL;
+       next_line_hilight = NULL;
+
+        read_settings();
+
+       nickmatch = nickmatch_init(hilight_nick_cache);
+       read_hilight_config();
+
+       signal_add_first("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped);
+       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 stripped", (SIGNAL_FUNC) sig_print_text_stripped);
+       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..0b7ac71
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ 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 list of all key bindings of which command is "key" -
+   this can be used to check fast if some command queue exists or not.
+   Format is _always_ in key1-key2-key3 format (like ^W-^N,
+   not ^W^N) */
+static GTree *key_states;
+/* List of all key combo names */
+static GSList *key_combos;
+static int key_config_frozen;
+
+struct KEYBOARD_REC {
+       /* example:
+          /BIND ^[ key meta
+          /BIND meta-O key meta2
+          /BIND meta-[ key meta2
+
+          /BIND meta2-C key right
+          /BIND ^W-meta-right /echo ^W Meta-right key pressed
+
+          When ^W Meta-Right is pressed, the full char combination
+          is "^W^[^[[C".
+
+          We'll get there with key states:
+            ^W - key_prev_state = NULL, key_state = NULL -> ^W
+            ^[ - key_prev_state = NULL, key_state = ^W -> meta
+            ^[ - key_prev_state = ^W, key_state = meta -> meta
+            [ - key_prev_state = ^W-meta, key_state = meta -> meta2
+            C - key_prev_state = ^W-meta, key_state = meta2 -> right
+            key_prev_state = ^W-meta, key_state = right -> ^W-meta-right
+
+          key_state is moved to key_prev_state if there's nothing else in
+          /BINDs matching for key_state-newkey.
+
+          ^X^Y equals to ^X-^Y, ABC equals to A-B-C unless there's ABC
+          named key. ^ can be used with ^^ and - with -- */
+       char *key_state, *key_prev_state;
+
+        /* GUI specific data sent in "key pressed" signal */
+        void *gui_data;
+};
+
+/* 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_not_null(keyboard->key_prev_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);
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               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_clear(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 KEY_REC *key_combo_find(const char *key)
+{
+       KEYINFO_REC *info;
+        GSList *tmp;
+
+       info = key_info_find("key");
+       if (info == NULL)
+               return NULL;
+
+       for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
+               KEY_REC *rec = tmp->data;
+
+               if (strcmp(rec->data, key) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+static void key_states_scan_key(const char *key, KEY_REC *rec, GString *temp)
+{
+       char **keys, **tmp, *p;
+
+       g_string_truncate(temp, 0);
+
+       /* meta-^W^Gfoo -> meta-^W-^G-f-o-o */
+       keys = g_strsplit(key, "-", -1);
+       for (tmp = keys; *tmp != NULL; tmp++) {
+               if (key_combo_find(*tmp)) {
+                        /* key combo */
+                       g_string_append(temp, *tmp);
+                        g_string_append_c(temp, '-');
+                        continue;
+               }
+
+               if (**tmp == '\0') {
+                        /* '-' */
+                       g_string_append(temp, "--");
+                        continue;
+               }
+
+               for (p = *tmp; *p != '\0'; p++) {
+                       g_string_append_c(temp, *p);
+
+                       if (*p == '^') {
+                                /* ctrl-code */
+                               if (p[1] != '\0')
+                                       p++;
+                               g_string_append_c(temp, *p);
+                       }
+
+                       g_string_append_c(temp, '-');
+               }
+       }
+       g_strfreev(keys);
+
+       if (temp->len > 0) {
+               g_string_truncate(temp, temp->len-1);
+
+               if (temp->str[1] == '-' || temp->str[1] == '\0')
+                        used_keys[(int) (unsigned char) temp->str[0]] = 1;
+               g_tree_insert(key_states, g_strdup(temp->str), rec);
+       }
+}
+
+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;
+}
+
+int key_states_search(const char *combo, const char *search)
+{
+       while (*search != '\0') {
+               if (*combo != *search)
+                       return *search - *combo;
+                search++; combo++;
+       }
+
+       return *combo == '\0' || *combo == '-' ? 0 : -1;
+}
+
+/* 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 *str;
+        int consumed;
+
+       g_return_val_if_fail(keyboard != NULL, FALSE);
+       g_return_val_if_fail(key != NULL && *key != '\0', FALSE);
+
+       if (keyboard->key_state == NULL) {
+               if (key[1] == '\0' &&
+                   !used_keys[(int) (unsigned char) key[0]]) {
+                        /* fast check - key not used */
+                       return FALSE;
+               }
+
+               rec = g_tree_search(key_states,
+                                   (GSearchFunc) key_states_search,
+                                   (void *) key);
+               if (rec == NULL ||
+                   (g_tree_lookup(key_states, (void *) key) != NULL &&
+                    strcmp(rec->info->id, "key") != 0)) {
+                       /* a single non-combo key was pressed */
+                       rec = g_hash_table_lookup(keys, key);
+                       if (rec == NULL)
+                               return FALSE;
+                       consumed = key_emit_signal(keyboard, rec);
+
+                       /* never consume non-control characters */
+                       return consumed && key[1] != '\0';
+               }
+       }
+
+       if (keyboard->key_state == NULL) {
+                /* first key in combo */
+               rec = g_tree_lookup(key_states, (void *) key);
+       } else {
+               /* continuing key combination */
+               str = g_strconcat(keyboard->key_state, "-", key, NULL);
+               rec = g_tree_lookup(key_states, str);
+               g_free(str);
+       }
+
+       if (rec != NULL && strcmp(rec->info->id, "key") == 0) {
+               /* combo has a specified name, use it */
+               g_free_not_null(keyboard->key_state);
+               keyboard->key_state = g_strdup(rec->data);
+       } else {
+               /* some unnamed key - move key_state after key_prev_state
+                  and replace key_state with this new key */
+               if (keyboard->key_prev_state == NULL)
+                       keyboard->key_prev_state = keyboard->key_state;
+               else {
+                       str = g_strconcat(keyboard->key_prev_state, "-",
+                                         keyboard->key_state, NULL);
+                       g_free(keyboard->key_prev_state);
+                       g_free(keyboard->key_state);
+                       keyboard->key_prev_state = str;
+               }
+
+               keyboard->key_state = g_strdup(key);
+       }
+
+        /* what to do with the key combo? */
+       str = keyboard->key_prev_state == NULL ?
+               g_strdup(keyboard->key_state) :
+               g_strconcat(keyboard->key_prev_state, "-",
+                           keyboard->key_state, NULL);
+
+       rec = g_tree_lookup(key_states, str);
+       if (rec != NULL) {
+               if (strcmp(rec->info->id, "key") == 0)
+                       rec = g_tree_lookup(key_states, rec->data);
+
+               if (rec != NULL) {
+                       /* full key combo */
+                       key_emit_signal(keyboard, rec);
+                       rec = NULL;
+               }
+       } else {
+                /* check that combo is possible */
+               rec = g_tree_search(key_states,
+                                   (GSearchFunc) key_states_search, str);
+       }
+
+       if (rec == NULL) {
+               /* a) key combo finished, b) unknown key combo, abort */
+               g_free_and_null(keyboard->key_prev_state);
+               g_free_and_null(keyboard->key_state);
+       }
+
+       g_free(str);
+        return TRUE;
+}
+
+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;
+       }
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next)
+               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_combos = NULL;
+        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..0f37ccf
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ 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} ", 6, { 1, 1, 1, 1, 1, 1 } },
+       { "servertag", "[$0] ", 1, { 0 } },
+       { "daychange", "Day changed to $[-2.0]{0} $3 $2", 4, { 1, 1, 1, 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", 1, { 0 } },
+       { "window_level", "Window level is now $0", 1, { 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 silc", 0 },
+       { "windows_layout_reset", "Layout of windows reset to defaults", 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 } },
+
+       /* ---- */
+       { 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 } },
+       { "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_nick", "{names_nick $0 $1}", 2, { 0, 0 } },
+       { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} voices, {hilight $4} normal}", 5, { 0, 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         IRC net    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 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-SILC", 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", "Loaded modules:", 0, },
+       { "module_line", "  $0", 1, { 0 } },
+       { "module_footer", "", 0, },
+       { "module_already_loaded", "Module {hilight $0} already loaded", 1, { 0 } },
+       { "module_load_error", "Error loading module {hilight $0}: $1", 2, { 0, 0 } },
+       { "module_invalid", "{hilight $0} isn't Irssi-SILC module", 1, { 0 } },
+       { "module_loaded", "Loaded module {hilight $0}", 1, { 0 } },
+       { "module_unloaded", "Unloaded module {hilight $0}", 1, { 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 },
+       { "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_changed", "Using theme {hilight $0} ($1) in this window", 2, { 0, 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", 1, { 0 } },
+       { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 },
+       { "perl_error", "Perl error: $0", 1, { 0 } },
+       { "bind_key", "$[10]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-SILC 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 },
+
+       { 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..e0f31f9
--- /dev/null
@@ -0,0 +1,194 @@
+#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_LEVEL,
+       TXT_WINDOWLIST_HEADER,
+       TXT_WINDOWLIST_LINE,
+       TXT_WINDOWLIST_FOOTER,
+       TXT_WINDOWS_LAYOUT_SAVED,
+       TXT_WINDOWS_LAYOUT_RESET,
+
+       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_FILL_3,
+
+       TXT_JOIN,
+       TXT_PART,
+       TXT_KICK,
+       TXT_QUIT,
+       TXT_QUIT_ONCE,
+       TXT_INVITE,
+       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_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_STARTED,
+       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_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_NOT_GOOD_IDEA,
+
+       TXT_FILL_11,
+
+       TXT_THEME_SAVED,
+       TXT_THEME_SAVE_FAILED,
+       TXT_THEME_NOT_FOUND,
+       TXT_THEME_CHANGED,
+       TXT_WINDOW_THEME_CHANGED,
+       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
+};
+
+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..38de158
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ 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;
+static int signal_print_text_stripped;
+static int signal_print_text;
+static int signal_print_text_finished;
+static int signal_print_format;
+static int signal_print_starting;
+
+static int sending_print_starting;
+
+static void print_line(TEXT_DEST_REC *dest, const char *text);
+
+static void printformat_module_dest(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 = dest->window->theme == NULL ? current_theme :
+               dest->window->theme;
+
+       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_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(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(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(current_theme, 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;
+
+       g_return_if_fail(dest != NULL);
+       g_return_if_fail(text != NULL);
+
+       tmp = format_get_level_tag(current_theme, dest);
+       str = format_add_linestart(text, tmp);
+       g_free_not_null(tmp);
+
+       /* send the plain text version for logging etc.. */
+       tmp = strip_codes(str);
+       signal_emit_id(signal_print_text_stripped, 2, dest, tmp);
+       g_free(tmp);
+
+       signal_emit_id(signal_print_text, 2, dest, str);
+       g_free(str);
+}
+
+/* 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)) {
+                               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)
+{
+       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)) {
+                       g_string_append_c(out, '%');
+                       g_string_append_c(out, *str);
+               }
+       }
+
+       ret = out->str;
+       g_string_free(out, FALSE);
+       return ret;
+}
+
+void printtext_dest(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);
+}
+
+/* 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(&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);
+       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(&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);
+       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_NOHILIGHT) == 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(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(current_theme, 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_print_text_finished, 1, dest->window);
+}
+
+static void sig_print_text_free(TEXT_DEST_REC *dest, const char *text)
+{
+        g_free_and_null(dest->hilight_color);
+}
+
+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 = signal_get_uniq_id("gui print text");
+       signal_print_text_stripped = signal_get_uniq_id("print text stripped");
+       signal_print_text = signal_get_uniq_id("print text");
+       signal_print_text_finished = signal_get_uniq_id("print text finished");
+       signal_print_format = signal_get_uniq_id("print format");
+       signal_print_starting = signal_get_uniq_id("print starting");
+
+       read_settings();
+       signal_add("print text", (SIGNAL_FUNC) sig_print_text);
+       signal_add_last("print text", (SIGNAL_FUNC) sig_print_text_free);
+       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("print text", (SIGNAL_FUNC) sig_print_text_free);
+       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..14f4656
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef __PRINTTEXT_H
+#define __PRINTTEXT_H
+
+#include "fe-windows.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_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 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_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);
+
+/* 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_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_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_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..ab768cc
--- /dev/null
@@ -0,0 +1,1184 @@
+/*
+ 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, 0);
+       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, 0);
+       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 */
+               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)++;
+}
+
+/* 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)) {
+                        /* if the data is empty, ignore the abstract */
+                       p = format+len;
+                       while (*p == ' ') p++;
+                       if (*p == '}') {
+                                *formatp = p+1;
+                               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 && 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, 0);
+       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 = 'n';
+       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/.silc/%s.theme", g_get_home_dir(), name);
+       if (stat(fname, &statbuf) != 0) {
+               /* check global config dir */
+               g_free(fname);
+               fname = g_strdup_printf(SYSCONFDIR"/irssi/%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", 0);
+       theme->default_real_color =
+               config_get_int(config, NULL, "default_real_color", 7);
+       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);
+}
+
+static void module_save(const char *module, MODULE_THEME_REC *rec,
+                        CONFIG_REC *config)
+{
+       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(config, "formats", TRUE);
+
+       node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
+       for (n = 0; formats[n].def != NULL; n++) {
+                if (rec->formats[n] != NULL) {
+                        config_node_set_str(config, node, formats[n].tag,
+                                            rec->formats[n]);
+                }
+        }
+
+        if (node->value == NULL) {
+                /* not modified, don't keep the empty section */
+                config_node_remove(config, fnode, node);
+                if (fnode->value == NULL)
+                        config_node_remove(config, config->mainnode, fnode);
+        }
+}
+
+static void theme_save(THEME_REC *theme)
+{
+       CONFIG_REC *config;
+       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);
+                }
+        }
+
+       g_hash_table_foreach(theme->modules, (GHFunc) module_save, config);
+
+        /* always save the theme to ~/.silc/ */
+       path = g_strdup_printf("%s/.silc/%s", g_get_home_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 */
+static void cmd_save(void)
+{
+       GSList *tmp;
+
+       for (tmp = themes; tmp != NULL; tmp = tmp->next) {
+               THEME_REC *theme = tmp->data;
+
+               theme_save(theme);
+       }
+}
+
+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_window(active_win, 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;
+
+       theme = settings_get_str("theme");
+       if (strcmp(current_theme->name, 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/.silc/default.theme",
+                                       g_get_home_dir());
+               current_theme = theme_create(fname, "default");
+               current_theme->default_color = 0;
+               current_theme->default_real_color = 7;
+                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");
+}
+
+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..96f2340
--- /dev/null
@@ -0,0 +1,65 @@
+#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 0 which means the default
+                             color set by terminal */
+       int default_real_color; /* default color to use with background set.
+                                  this shouldn't be 0, unless black is really
+                                  wanted. default is 7 (white). */
+       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, 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..2713cc7
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ 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 "signals.h"
+#include "line-split.h"
+#include "misc.h"
+#include "settings.h"
+
+unsigned char translation_in[256], translation_out[256];
+
+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) \
+       (isdigit(a) ? ((a)-'0') : (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) 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();
+       return pos == 512;
+}
+
+static void read_settings(void)
+{
+       translation_read(settings_get_str("translation"));
+}
+
+void translation_init(void)
+{
+       translation_reset();
+
+       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..17cf65e
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+ 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 cmd_window(const char *data, void *server, WI_ITEM_REC *item)
+{
+       if (is_numeric(data, 0)) {
+                signal_emit("command window refnum", 3, data, server, item);
+               return;
+       }
+
+       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' ? active_win->refnum : 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)
+                       window_destroy(rec);
+
+                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 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' &&
+           (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, server->tag);
+       }
+
+       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;
+
+        if (is_numeric(data, '\0')) {
+                /* move current window item to specified window */
+                window = window_find_refnum(atoi(data));
+        } else {
+                /* move specified window item to current window */
+                item = window_item_find(server, data);
+                window = active_win;
+        }
+        if (window != NULL && item != NULL)
+                window_item_set_active(window, item);
+}
+
+/* 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)
+{
+        window_set_name(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 windows_move_left(WINDOW_REC *move_window)
+{
+       WINDOW_REC *window;
+       int refnum;
+
+       window_set_refnum(move_window, windows_refnum_last()+1);
+       for (refnum = 2;; refnum++) {
+               window = window_find_refnum(refnum);
+               if (window == NULL) break;
+
+               window_set_refnum(window, refnum-1);
+       }
+}
+
+/* we're moving the last window to first - make some space so we can use the
+   refnum 1 */
+static void windows_move_right(WINDOW_REC *move_window)
+{
+       WINDOW_REC *window;
+       int refnum;
+
+       /* 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 = 1;
+       while (window_find_refnum(refnum) != NULL) refnum++;
+
+       refnum--;
+       while (refnum > 0) {
+               window = window_find_refnum(refnum);
+               g_return_if_fail(window != NULL);
+               window_set_refnum(window, window == move_window ? 1 : refnum+1);
+
+               refnum--;
+       }
+}
+
+static void cmd_window_move_left(void)
+{
+       int refnum;
+
+       refnum = window_refnum_prev(active_win->refnum, TRUE);
+       if (refnum != -1) {
+               window_set_refnum(active_win, refnum);
+               return;
+       }
+
+       windows_move_left(active_win);
+}
+
+static void cmd_window_move_right(void)
+{
+       int refnum;
+
+       refnum = window_refnum_next(active_win->refnum, TRUE);
+       if (refnum != -1) {
+               window_set_refnum(active_win, refnum);
+               return;
+       }
+
+        windows_move_right(active_win);
+}
+
+/* SYNTAX: WINDOW MOVE <number>|left|right */
+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 <name> */
+static void cmd_window_theme(const char *data)
+{
+       THEME_REC *theme;
+
+       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);
+       }
+}
+
+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 *tmp;
+
+        old = active_win;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+                active_win = rec;
+               signal_emit("send command", 3, data, rec->active_server,
+                           rec->active);
+       }
+        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 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 move", NULL, (SIGNAL_FUNC) cmd_window_move);
+       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 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");
+}
+
+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 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 move", (SIGNAL_FUNC) cmd_window_move);
+       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 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..d772652
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ 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);
+
+        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 (!automatic || g_slist_length(window->items) == 1) {
+                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 (g_slist_find(window->items, item) == 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);
+
+       signal_emit("window item destroy", 2, window, 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;
+       GSList *tmp, *sorted;
+       int clear_waiting, reuse_unused_windows;
+
+       g_return_if_fail(item != NULL);
+
+       reuse_unused_windows =
+               !settings_get_bool("autoclose_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 &&
+                   window_bind_find(rec, item->server->tag, item->name)) {
+                       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 */
+               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);
+
+       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..814127f
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ 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 void sig_window_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 */
+               chat_protocol_find(chat_type)->query_create(tag, name, TRUE);
+       }
+}
+
+static void window_add_items(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       GSList *tmp;
+       char *type;
+
+       if (node == NULL)
+               return;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               CONFIG_NODE *node = tmp->data;
+
+               type = config_node_get_str(node, "type", NULL);
+               if (type != NULL) {
+                       signal_emit("window restore item", 3,
+                                   window, type, node);
+               }
+       }
+}
+
+void windows_layout_restore(void)
+{
+       WINDOW_REC *window;
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       node = iconfig_node_traverse("windows", FALSE);
+       if (node == NULL) return;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               CONFIG_NODE *node = tmp->data;
+
+               window = window_create(NULL, TRUE);
+               window_set_refnum(window, atoi(node->key));
+                window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE);
+               window_set_name(window, config_node_get_str(node, "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("window restore", 2, window, node);
+       }
+
+       signal_emit("windows restored", 0);
+}
+
+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->name != NULL)
+               iconfig_node_set_str(node, "name", window->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("window save", 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("windows saved", 0);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_WINDOWS_LAYOUT_SAVED);
+}
+
+void windows_layout_reset(void)
+{
+       iconfig_set_str(NULL, "windows", NULL);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_WINDOWS_LAYOUT_RESET);
+}
+
+void windows_layout_init(void)
+{
+       signal_add("window restore item", (SIGNAL_FUNC) sig_window_restore_item);
+}
+
+void windows_layout_deinit(void)
+{
+       signal_remove("window restore item", (SIGNAL_FUNC) sig_window_restore_item);
+}
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..d2c62f23e3f65cd18962bc6feece623f09181180 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} ($1)", 2, { 0, 0 } },
+       { "kicked", "{nick $0} has been kicked off channel {channel $1} ($2)", 3, { 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 } },
+       { "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 the {nick $0} are now protected with the 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 } },
+       { "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_attach", "It is also possible that someone is performing 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 incompatible client version (it may be too old or too new) ", 0 },
+       { "ke_unsupported_public_key", "Server does not support your public key type", 0 },
+       { "ke_unknown_group", "Server does not support one of your proposed KE group", 0 },
+       { "ke_unknown_cipher", "Server does not support one of your proposed cipher", 0 },
+       { "ke_unknown_pkcs", "Server does not support one of your proposed PKCS", 0 },
+       { "ke_unknown_hash_function", "Server does not support one of your proposed hash function", 0 },
+       { "ke_unknown_hmac", "Server 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 meen 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..c1d94a34a9b3c0eedd43053348032dc89314f02c 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_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_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) {  }
index ef0f561662ab1904920eed23288a4e12081f56ab..326ec232bee84477b33e74d0c13c2c1fd2a1fff6 100644 (file)
@@ -1,6 +1,8 @@
 bin_PROGRAMS = silc
 
-INCLUDES = \
+include $(top_srcdir)/Makefile.defines.in
+
+ADD_INCLUDES = \
        $(GLIB_CFLAGS) \
        -I$(top_srcdir)/src \
        -I$(top_srcdir)/src/core/ \
@@ -12,13 +14,15 @@ INCLUDES = \
 
 silc_DEPENDENCIES = @COMMON_LIBS@
 
+LIBS = $(SILC_COMMON_LIBS)
 silc_LDADD = \
        @COMMON_LIBS@ \
        @PERL_LINK_LIBS@ \
        @PERL_FE_LINK_LIBS@ \
        $(PROG_LIBS) \
         $(CURSES_LIBS) \
-       -L../../../lib -lsilcclient -lsilc
+       -L../../../lib -lsilcclient
+
 
 silc_SOURCES = \
         gui-entry.c \
index 9a199cdded559dc24af7bd7c350264dbb378f05b..5d7ad0d734b150b306f74cdf072b46b17580da19 100644 (file)
@@ -217,9 +217,10 @@ 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);
 
@@ -245,10 +246,11 @@ static void signal_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
                }
                gui_window_reparent(window, active_mainwin);
        }
-       active_mainwin->active = window;
 
-       if (old_window != NULL && !is_window_visible(old_window))
+       old_window = active_mainwin->active;
+       if (old_window != NULL)
                 textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
+        active_mainwin->active = window;
 
        textbuffer_view_set_window(WINDOW_GUI(window)->view,
                                   parent->curses_win);
index 9112b568506c17e3eb93b4e53051975b3c30561a..a4203e479468023cca5cd8cd9bf2ab530f759661 100644 (file)
@@ -148,10 +148,12 @@ static void textui_finish_init(void)
 #endif
        signal_emit("irssi init finished", 0);
 
+#if 0
        if (display_firsttimer) {
                printtext_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                 "%s", firsttimer_text);
        }
+#endif
 
        screen_refresh_thaw();
 }
@@ -204,7 +206,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/.silc/default.theme", g_get_home_dir());
        f = fopen(path, "r+");
        if (f == NULL) {
                g_free(path);
@@ -220,7 +222,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 ~/.silc/ 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");
@@ -238,7 +240,7 @@ static void check_files(void)
        struct stat statbuf;
         char *path;
 
-        path = g_strdup_printf("%s/.irssi", g_get_home_dir());
+        path = g_strdup_printf("%s/.silc", g_get_home_dir());
        if (stat(path, &statbuf) != 0) {
                /* ~/.irssi doesn't exist, first time running irssi */
                display_firsttimer = TRUE;
@@ -280,6 +282,7 @@ int main(int argc, char **argv)
 
        textui_init();
        args_execute(argc, argv);
+       silc_init_finish();
 
        if (!init_screen())
                g_error("Can't initialize screen handling, quitting.\n");
index f3a74080f4d0d5bb12ebaa931bfcfd203f299acf..449f20a9197860488861cd7351e68a2e61fc355d 100644 (file)
@@ -588,9 +588,6 @@ 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);
@@ -600,6 +597,12 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
        view->width = width;
        view->height = height;
 
+
+        if (view->buffer->lines == NULL) {
+                 view->empty_linecount = height;
+                 return;
+        }
+
        textbuffer_view_init_bottom(view);
 
        /* check that we didn't scroll lower than bottom startline.. */
@@ -841,7 +844,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;
@@ -852,14 +855,14 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        if (rec.remove_list != NULL) {
                GList *pos = g_list_find(view->buffer->lines, line);
 
-               newline = pos == NULL || pos->prev == NULL ? NULL :
+               new_line = pos == NULL || pos->prev == NULL ? NULL :
                        (pos->next == NULL ? pos->prev->data :
                         pos->next->data);
                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);
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..769899e
--- /dev/null
@@ -0,0 +1,37 @@
+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 \
+       clientconfig.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 \
+       clientconfig.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..a3af5d4
--- /dev/null
@@ -0,0 +1,1272 @@
+/*
+
+  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,
+                               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"));
+
+  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_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == sender) {
+       nick = silc_nicklist_insert(chanrec, chu, FALSE);
+       break;
+      }
+    }
+  }
+
+  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 objecet 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;
+    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;
+  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_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (!chu->client->nickname)
+      continue;
+    if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+      founder = chu->client;
+    silc_nicklist_insert(chanrec, chu, FALSE);
+  }
+
+  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;
+      uint32 idle, mode;
+      SilcBuffer channels;
+      SilcClientEntry client_entry;
+      
+      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;
+      
+      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);
+      
+      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);
+       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);
+      }
+    }
+    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);
+
+      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)
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
+
+      if (mode & SILC_UMODE_ROUTER_OPERATOR)
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
+    }
+    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: 
+    {
+      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_list_start(channel->clients);
+      while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+       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);
+      }
+    }
+    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;
+      
+      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);
+       
+       silc_verify_public_key_internal(client, conn, 
+                                       (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_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);
+}
+
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. */
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  char *filename;
+  char *entity;
+  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);
+  }
+
+  silc_free(verify->filename);
+  silc_free(verify->entity);
+  silc_free(verify->pk);
+  silc_free(verify);
+}
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context)
+{
+  int i;
+  char file[256], filename[256], *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(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);
+  babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->client = client;
+  verify->conn = conn;
+  verify->filename = strdup(filename);
+  verify->entity = strdup(entity);
+  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(filename, &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, 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 */
+    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)) {
+       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                          SILCTXT_PUBKEY_RECEIVED, 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, 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, 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, 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/clientconfig.c b/apps/irssi/src/silc/core/clientconfig.c
new file mode 100644 (file)
index 0000000..450020a
--- /dev/null
@@ -0,0 +1,846 @@
+/*
+
+  serverconfig.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 "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 "window-item-def.h"
+
+#include "fe-common/core/printtext.h"
+
+/* 
+   All possible configuration sections for SILC client.
+*/
+SilcClientConfigSection silc_client_config_sections[] = {
+  { "[cipher]", 
+    SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
+  { "[pkcs]", 
+    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 },
+  
+  { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 }
+};
+
+/* Allocates a new configuration object, opens configuration file and
+   parses the file. The parsed data is returned to the newly allocated
+   configuration object. */
+
+SilcClientConfig silc_client_config_alloc(char *filename)
+{
+  SilcClientConfig new;
+  SilcBuffer buffer;
+  SilcClientConfigParse config_parse;
+  char *str;
+
+  SILC_LOG_DEBUG(("Allocating new configuration object"));
+
+  new = silc_calloc(1, sizeof(*new));
+  new->filename = filename;
+
+  /* Open configuration file and parse it */
+  config_parse = NULL;
+  buffer = NULL;
+  str = convert_home(filename);
+  silc_config_open(str, &buffer);
+  g_free(str);
+  if (!buffer)
+    goto fail;
+  if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE)
+    goto fail;
+  if ((silc_client_config_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_client_config_free(SilcClientConfig config)
+{
+  if (config) {
+
+    silc_free(config);
+  }
+}
+
+/* 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. */
+
+int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer, 
+                            SilcClientConfigParse *return_config)
+{
+  int i, begin;
+  int linenum;
+  char line[1024], *cp;
+  SilcClientConfigSection *cptr = NULL;
+  SilcClientConfigParse 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_client_config_sections; cptr->section; cptr++)
+       if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
+         break;
+
+      if (!cptr->section) {
+       fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
+                       config->filename, linenum, cp);
+       return FALSE;
+      }
+
+      break;
+    default:
+      /*
+       * Start of a configuration line
+       */
+
+      if (!cptr) {
+       fprintf(stderr, "%s:%d: Unknown start of a section `%s'\n", 
+                       config->filename, linenum, cp);
+       return FALSE;
+      }        
+
+      /* Handle config section */
+      if (cptr->type != SILC_CLIENT_CONFIG_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;
+    }
+  }
+  
+  /* 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;
+}
+
+/* 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_client_config_parse_lines(SilcClientConfig config, 
+                                  SilcClientConfigParse parse_config)
+{
+  int ret, check = FALSE;
+  char *tmp;
+  SilcClientConfigParse pc = parse_config;
+  SilcBuffer line;
+
+  SILC_LOG_DEBUG(("Parsing configuration lines"));
+  
+  if (!config)
+    return FALSE;
+  
+  while(pc) {
+    check = FALSE;
+    line = pc->line;
+
+    /* Get number of tokens in line (command section is handeled
+       specially and has no tokens at all). */
+    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;
+    }
+
+    /* Parse the line */
+    switch(pc->section->type) {
+    case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER:
+
+      if (!config->cipher) {
+       config->cipher = silc_calloc(1, sizeof(*config->cipher));
+       config->cipher->next = NULL;
+       config->cipher->prev = NULL;
+      } else {
+       if (!config->cipher->next) {
+         config->cipher->next = 
+           silc_calloc(1, sizeof(*config->cipher->next));
+         config->cipher->next->next = NULL;
+         config->cipher->next->prev = config->cipher;
+         config->cipher = config->cipher->next;
+       }
+      }
+
+      /* 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 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);
+
+      /* 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);
+
+      check = TRUE;
+      break;
+
+    case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS:
+
+      if (!config->pkcs) {
+       config->pkcs = silc_calloc(1, sizeof(*config->pkcs));
+       config->pkcs->next = NULL;
+       config->pkcs->prev = NULL;
+      } else {
+       if (!config->pkcs->next) {
+         config->pkcs->next = 
+           silc_calloc(1, sizeof(*config->pkcs->next));
+         config->pkcs->next->next = NULL;
+         config->pkcs->next->prev = config->pkcs;
+         config->pkcs = config->pkcs->next;
+       }
+      }
+
+      /* 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;
+      }
+
+      check = TRUE;
+      break;
+
+    case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION:
+
+      if (!config->hash_func) {
+       config->hash_func = silc_calloc(1, sizeof(*config->hash_func));
+       config->hash_func->next = NULL;
+       config->hash_func->prev = NULL;
+      } else {
+       if (!config->hash_func->next) {
+         config->hash_func->next = 
+           silc_calloc(1, sizeof(*config->hash_func->next));
+         config->hash_func->next->next = NULL;
+         config->hash_func->next->prev = config->hash_func;
+         config->hash_func = config->hash_func->next;
+       }
+      }
+
+      /* 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;
+      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) {
+       config->conns = silc_calloc(1, sizeof(*config->conns));
+       config->conns->next = NULL;
+       config->conns->prev = NULL;
+      } else {
+       if (!config->conns->next) {
+         config->conns->next = silc_calloc(1, sizeof(*config->conns));
+         config->conns->next->next = NULL;
+         config->conns->next->prev = config->conns;
+         config->conns = config->conns->next;
+       }
+      }
+      
+      /* Get host */
+      ret = silc_config_get_token(line, &config->conns->host);
+      if (ret < 0)
+       break;
+      if (ret == 0)
+       /* Any host */
+       config->conns->host = strdup("*");
+
+      /* Get authentication method */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret) {
+       if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) &&
+           strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) {
+         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
+                 config->filename, pc->linenum, tmp);
+         break;
+       }
+
+       if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
+         config->conns->auth_meth = SILC_AUTH_PASSWORD;
+
+       if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
+         config->conns->auth_meth = SILC_AUTH_PUBLIC_KEY;
+
+       silc_free(tmp);
+      }
+
+      /* Get authentication data */
+      ret = silc_config_get_token(line, &config->conns->auth_data);
+      if (ret < 0)
+       break;
+
+      /* Get port */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret) {
+       config->conns->port = atoi(tmp);
+       silc_free(tmp);
+      }
+
+      check = TRUE;
+      break;
+
+    case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE:
+    default:
+      break;
+    }
+
+    /* Check for error */
+    if (check == FALSE) {
+      /* Line could not be parsed */
+      fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
+      break;
+    }
+
+    pc = pc->next;
+  }
+
+  if (check == FALSE)
+    return FALSE;;
+
+  /* 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->hmac && config->hmac->prev)
+    config->hmac = config->hmac->prev;
+  while (config->conns && config->conns->prev)
+    config->conns = config->conns->prev;
+  
+  SILC_LOG_DEBUG(("Done"));
+  
+  return TRUE;
+}
+
+/* Registers configured ciphers. These can then be allocated by the
+   client when needed. */
+
+bool silc_client_config_register_ciphers(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg;
+  SilcClient client = config->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. 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_is_supported(alg->alg_name)) {
+       SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
+       silc_client_stop(client);
+       exit(1);
+      }
+
+#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;
+      cipher.block_len = alg->block_len;
+      cipher.key_len = alg->key_len * 8;
+
+      sim = silc_sim_alloc();
+      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_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_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_name,
+                                               SILC_CIPHER_SIM_ENCRYPT_CBC));
+       SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
+        cipher.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_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 */
+       sims = silc_realloc(sims,
+                                  sizeof(*sims) * 
+                                  (sims_count + 1));
+       sims[sims_count] = sim;
+       sims_count++;
+
+       silc_free(alg_name);
+      } else {
+       SILC_LOG_ERROR(("Error configuring ciphers"));
+       silc_client_stop(client);
+       exit(1);
+      }
+
+      /* Register the cipher */
+      silc_cipher_register(&cipher);
+#endif
+    }
+
+    alg = alg->next;
+  }
+
+  return TRUE;
+}
+
+/* Registers configured PKCS's. */
+
+bool silc_client_config_register_pkcs(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg = config->pkcs;
+  SilcClient client = config->client;
+
+  SILC_LOG_DEBUG(("Registering configured PKCS"));
+
+  if (!alg)
+    return FALSE;
+
+  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);
+    }
+
+    alg = alg->next;
+  }
+
+  return TRUE;
+}
+
+/* Registers configured hash funtions. These can then be allocated by the
+   client when needed. */
+
+bool silc_client_config_register_hashfuncs(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg;
+  SilcClient client = config->client;
+
+  SILC_LOG_DEBUG(("Registering configured hash functions"));
+
+  if (!config->hash_func)
+    return FALSE;
+
+  alg = config->hash_func;
+  while(alg) {
+    if (!alg->sim_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);
+      }
+#ifdef SILC_SIM
+    } else {
+      /* Load (try at least) the hash SIM module */
+      SilcHashObject hash;
+      SilcSimContext *sim;
+
+      memset(&hash, 0, sizeof(hash));
+      hash.name = alg->alg_name;
+      hash.block_len = alg->block_len;
+      hash.hash_len = alg->key_len;
+
+      sim = silc_sim_alloc();
+      sim->type = SILC_SIM_HASH;
+      sim->libname = alg->sim_name;
+
+      if ((silc_sim_load(sim))) {
+       hash.init = 
+         silc_sim_getsym(sim, silc_sim_symname(alg->alg_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_HASH_SIM_UPDATE));
+       SILC_LOG_DEBUG(("update=%p", hash.update));
+        hash.final = 
+         silc_sim_getsym(sim, silc_sim_symname(alg->alg_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_HASH_SIM_CONTEXT_LEN));
+       SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
+
+       /* Put the SIM to the table of all SIM's in client */
+       sims = silc_realloc(sims,
+                                  sizeof(*sims) * 
+                                  (sims_count + 1));
+       sims[sims_count] = sim;
+       sims_count++;
+      } else {
+       SILC_LOG_ERROR(("Error configuring hash functions"));
+       silc_client_stop(client);
+       exit(1);
+      }
+
+      /* 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;
+  SilcClient client = config->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, 
+                                  char *host, int port)
+{
+  int i;
+  SilcClientConfigSectionConnection *conn = NULL;
+
+  SILC_LOG_DEBUG(("Finding connection"));
+
+  if (!host)
+    return NULL;
+
+  if (!config->conns)
+    return NULL;
+
+  conn = config->conns;
+  for (i = 0; conn; i++) {
+    if (silc_string_compare(conn->host, host))
+      break;
+    conn = conn->next;
+  }
+
+  if (!conn)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Found match"));
+
+  return conn;
+}
diff --git a/apps/irssi/src/silc/core/clientconfig.h b/apps/irssi/src/silc/core/clientconfig.h
new file mode 100644 (file)
index 0000000..a9a7f19
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+
+  clientconfig.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 CLIENTCONFIG_H
+#define CLIENTCONFIG_H
+
+/* Holds information of configured algorithms */
+typedef struct SilcClientConfigSectionAlgStruct {
+  char *alg_name;
+  char *sim_name;
+  uint32 block_len;
+  uint32 key_len;
+  struct SilcClientConfigSectionAlgStruct *next;
+  struct SilcClientConfigSectionAlgStruct *prev;
+#define SILC_CLIENT_CONFIG_MODNAME "builtin"
+} SilcClientConfigSectionAlg;
+
+/* Holds all server connections from config file */
+typedef struct SilcClientConfigSectionConnectionStruct {
+  char *host;
+  int auth_meth;
+  char *auth_data;
+  uint16 port;
+  struct SilcClientConfigSectionConnectionStruct *next;
+  struct SilcClientConfigSectionConnectionStruct *prev;
+#define SILC_CLIENT_CONFIG_AUTH_METH_PASSWD "passwd"
+#define SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY "pubkey"
+} SilcClientConfigSectionConnection;
+
+/* 
+   SILC Client Config object.
+
+   This object holds all the data parsed from the SILC client configuration
+   file. This is mainly used at the initialization of the client.
+
+*/
+typedef struct {
+  /* Pointer back to the client */
+  void *client;
+
+  /* Filename of the configuration file */
+  char *filename;
+
+  /* Configuration sections */
+  SilcClientConfigSectionAlg *cipher;
+  SilcClientConfigSectionAlg *pkcs;
+  SilcClientConfigSectionAlg *hash_func;
+  SilcClientConfigSectionAlg *hmac;
+  SilcClientConfigSectionConnection *conns;
+} SilcClientConfigObject;
+
+typedef SilcClientConfigObject *SilcClientConfig;
+
+/* Configuration section type enumerations. */
+typedef enum {
+  SILC_CLIENT_CONFIG_SECTION_TYPE_NONE = 0,
+  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,
+} SilcClientConfigSectionType;
+
+/* SILC Configuration Section structure. */
+typedef struct {
+  const char *section;
+  SilcClientConfigSectionType type;
+  int maxfields;
+} SilcClientConfigSection;
+
+/* List of all possible config sections in SILC client */
+extern SilcClientConfigSection silc_client_config_sections[];
+
+/* Structure used in parsing the configuration lines. The line is read
+   from a file to this structure before parsing it further. */
+typedef struct SilcClientConfigParseStruct {
+  SilcBuffer line;
+  int linenum;
+  SilcClientConfigSection *section;
+  struct SilcClientConfigParseStruct *next;
+  struct SilcClientConfigParseStruct *prev;
+} *SilcClientConfigParse;
+
+/* Prototypes */
+SilcClientConfig silc_client_config_alloc(char *filename);
+void silc_client_config_free(SilcClientConfig config);
+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(uint32 checkmask);
+void silc_client_config_setlogfiles(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);
+
+#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..622fb76
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+
+  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$ */
+
+#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));
+
+  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;
+    }
+    
+#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(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..c02d61d
--- /dev/null
@@ -0,0 +1,1321 @@
+/*
+  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, *channel;
+  SILC_CHANNEL_REC *chanrec;
+
+  list = g_strsplit(channels, ",", -1);
+  for (tmp = list; *tmp != NULL; tmp++) {
+    channel = **tmp == '#' ? g_strdup(*tmp) :
+      g_strconcat("#", *tmp, NULL);
+
+    chanrec = silc_channel_find(server, channel);
+    if (chanrec) {
+      g_free(channel);
+      continue;
+    }
+
+    silc_command_exec(server, "JOIN", channel);
+    g_free(channel);
+  }
+
+  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 user;
+
+      silc_list_start(chanrec->entry->clients);
+      while ((user = silc_list_get(chanrec->entry->clients)) != NULL)
+       if (user->client == client) {
+         nickrec = silc_nicklist_insert(chanrec, user, TRUE);
+         break;
+       }
+    }
+  }
+
+  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;
+  SilcClientEntry client;
+  SilcChannelEntry channel;
+  char *topic;
+  char userhost[256];
+
+  client = va_arg(va, SilcClientEntry);
+  topic = va_arg(va, char *);
+  channel = va_arg(va, SilcChannelEntry);
+
+  silc_server_free_ftp(server, client);
+
+  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);
+  }
+
+  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);
+}
+
+/*
+ * "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->cipher->name,
+                           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++) {
+    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");
+  }
+}
+
+/*
+ * "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;
+  SilcChannelEntry channel_entry;
+  char *tmp;
+  SILC_CHANNEL_REC *chanrec;
+
+  client_entry = va_arg(va, SilcClientEntry);
+  tmp = va_arg(va, char *);
+  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, 
+                      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,
+                      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;
+    
+  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..767cb0b
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+
+  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_debug = 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_pkcs = NULL;
+static char *opt_keyfile = NULL;
+static int opt_bits = 0;
+
+static int idletag;
+
+SilcClient silc_client = NULL;
+SilcClientConfig silc_config = NULL;
+extern SilcClientOperations ops;
+extern int silc_debug;
+#ifdef SILC_SIM
+/* SIM (SILC Module) table */
+SilcSimContext **sims = NULL;
+uint32 sims_count = 0;
+#endif
+
+static int my_silc_scheduler(void)
+{
+  silc_schedule_one(silc_client->schedule, 0);
+  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));
+}
+
+/* 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 void silc_log_info(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
+static void silc_log_warning(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
+static void silc_log_error(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
+/* 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", 'H', 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_NONE, &opt_debug, 0,
+      "Enable debugging", NULL },
+    { "version", 'V', POPT_ARG_NONE, &opt_version, 0,
+      "Show version", NULL },
+    { NULL, '\0', 0, NULL }
+  };
+
+  args_register(options);
+}
+
+static void silc_nickname_format_parse(const char *nickname,
+                                      char **ret_nickname)
+{
+  silc_parse_userfqdn(nickname, ret_nickname, NULL);
+}
+
+/* Finalize init. Called from src/fe-text/silc.c */
+
+void silc_core_init_finish(void)
+{
+  CHAT_PROTOCOL_REC *rec;
+  SilcClientParams params;
+
+  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); 
+  }
+
+  silc_debug = opt_debug;
+  silc_log_set_callbacks(silc_log_info, silc_log_warning,
+                        silc_log_error, NULL);
+
+  /* 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);
+
+  /* Load local config file */
+  silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
+
+  /* Get user information */
+  silc_client->username = g_strdup(settings_get_str("user_name"));
+  silc_client->hostname = silc_net_localhost();
+  silc_client->realname = g_strdup(settings_get_str("real_name"));
+
+  /* Register all configured ciphers, PKCS and hash functions. */
+  if (silc_config) {
+    silc_config->client = silc_client;
+    if (!silc_client_config_register_ciphers(silc_config))
+      silc_cipher_register_default();
+    if (!silc_client_config_register_pkcs(silc_config))
+      silc_pkcs_register_default();
+    if (!silc_client_config_register_hashfuncs(silc_config))
+      silc_hash_register_default();
+    if (!silc_client_config_register_hmacs(silc_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) {
+    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->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);
+}
+
+/* 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..15ac713
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __SILC_CORE_H
+#define __SILC_CORE_H
+
+#include "clientconfig.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;
+extern SilcClientConfig silc_config;
+
+#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..f9f5854
--- /dev/null
@@ -0,0 +1,914 @@
+/*
+  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, "Unknown nick: %s", 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, "Unknown nick: %s", 
+                 rec->nick);
+       silc_free(nickname);
+       goto out;
+      }
+      silc_free(nickname);
+    }
+
+    target = clients[0];
+
+    /* 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(const char *data)
+{
+  return *data == '#';
+}
+
+const char *get_nick_flags(void)
+{
+  return "@\0\0";
+}
+
+static void send_message(SILC_SERVER_REC *server, char *target, char *msg)
+{
+  g_return_if_fail(server != NULL);
+  g_return_if_fail(target != NULL);
+  g_return_if_fail(msg != NULL);
+
+  if (*target == '#')
+    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));
+  if (!silc_client_start_key_exchange(silc_client, conn, fd)) {
+    /* some internal error occured */
+    server_disconnect(SERVER(server));
+    signal_stop();
+    return;
+  }
+
+  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;
+  
+  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;
+
+  if (!server_start_connect((SERVER_REC *) server)) {
+    server_connect_free(SERVER_CONNECT(conn));
+    g_free(server);
+    return NULL;
+  }
+
+  server->ftp_sessions = silc_dlist_init();
+
+  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|agreement|negotiate [<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 */
+
+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(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 */
+  (*cmd->cb)(ctx, NULL);
+}
+
+/* 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();
+}
+
+static void event_text(const char *line, SILC_SERVER_REC *server,
+                      WI_ITEM_REC *item)
+{
+  char *str;
+
+  g_return_if_fail(line != NULL);
+
+  if (!IS_SILC_ITEM(item))
+    return;
+
+  str = g_strdup_printf("%s %s", item->name, line);
+  signal_emit("command msg", 3, str, server, item);
+  g_free(str);
+
+ 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;
+
+  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]);
+
+    ftp = silc_calloc(1, sizeof(*ftp));
+    ftp->session_id = 
+      silc_client_file_send(silc_client, conn, silc_client_file_monitor, 
+                           server, local_ip, local_port, 
+                           client_entry, argv[2]);
+
+    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;
+
+    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);
+  signal_add("send text", (SIGNAL_FUNC) event_text);
+  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);
+  signal_remove("send text", (SIGNAL_FUNC) event_text);
+  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..7add2f5
--- /dev/null
@@ -0,0 +1,73 @@
+#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;
+} 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_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..5c42734f73d5a067aa15904e77bc56d2a08be6c5 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,
+                                    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;
+
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
+    return TRUE;
+
+  silc_server_command_send_status_reply(cmd, command,
+                                       SILC_STATUS_ERR_NOT_REGISTERED);
+  silc_server_command_free(cmd);
+  return FALSE;
+}
+
+/* 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)
+{
+  SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
+  SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
+
+  /* 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);
+
+  silc_free(timeout);
+}
+
+/* Processes received command packet. */
+
+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);
+  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);
+
+  /* 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);
+}
+
+/* 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);
+  }
+}
 
-/* 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.
+/* Duplicate Command Context by adding reference counter. The context won't
+   be free'd untill it hits zero. */
 
-   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. */
+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;
+}
 
-void silc_server_command_pending(SilcCommand reply_cmd,
+/* 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'. */
+
+void silc_server_command_pending(SilcServer server,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcServerPendingDestructor destructor,
                                 SilcCommandCb callback,
                                 void *context)
 {
-  SilcServerCommandPending *reply, *r;
+  SilcServerCommandPending *reply;
 
   reply = silc_calloc(1, sizeof(*reply));
   reply->reply_cmd = reply_cmd;
+  reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
+  reply->destructor = destructor;
+  silc_dlist_add(server->pending_commands, reply);
+}
 
-  if (silc_command_pending == NULL) {
-    silc_command_pending = reply;
-    return;
-  }
+/* Deletes pending command by reply command type. */
+
+void silc_server_command_pending_del(SilcServer server,
+                                    SilcCommand reply_cmd,
+                                    uint16 ident)
+{
+  SilcServerCommandPending *r;
 
-  for (r = silc_command_pending; r; r = r->next) {
-    if (r->next == NULL) {
-      r->next = reply;
+  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;
     }
   }
 }
 
-/* Deletes pending command by reply command type. */
+/* Checks for pending commands and marks callbacks to be called from
+   the command reply function. Returns TRUE if there were pending command. */
 
-void silc_server_command_pending_del(SilcCommand reply_cmd)
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+                                 SilcServerCommandReplyContext ctx,
+                                 SilcCommand command, 
+                                 uint16 ident,
+                                 uint32 *callbacks_count)
 {
-  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;
-    }
-
-    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;
-      }
+  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;
+      callbacks[i].destructor = r->destructor;
+      ctx->ident = ident;
+      i++;
     }
   }
+
+  *callbacks_count = i;
+  return callbacks;
 }
 
-/* Free's the command context allocated before executing the command */
+/* Destructor function for pending callbacks. This is called when using
+   pending commands to free the context given for the pending command. */
 
-static void silc_server_command_free(SilcServerCommandContext cmd)
+static void silc_server_command_destructor(void *context)
 {
-  if (cmd) {
-    silc_command_free_payload(cmd->payload);
-    silc_free(cmd);
-  }
+  silc_server_command_free((SilcServerCommandContext)context);
 }
 
-/* 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,
+                                    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) {
+    /* Send the error message */
+    silc_server_command_send_status_reply(cmd, command, status);
+    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);
+      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);
+           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;
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  else
+    *count = 0;
+
+  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;
+
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
+
+    if (!entry || (entry->nickname && entry->username && entry->userinfo) ||
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+       !entry->router)
+      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_destructor,
+                                 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;
+      }
     }
-    count = atoi(tmp);
   }
 
-  /* 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) {
+  /* 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_destructor,
+                               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);
 
-    /* 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) {
+  return no_res;
+}
 
-      goto ok;
+static void
+silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
+                                    SilcClientEntry *clients,
+                                    uint32 clients_count,
+                                    int count)
+{
+  SilcServer server = cmd->server;
+  char *tmp;
+  int i, k, len;
+  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];
+  SilcSocketConnection hsock;
+
+  len = 0;
+  for (i = 0; i < clients_count; i++)
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      len++;
+
+  if (len == 0 && clients_count) {
+    entry = clients[0];
+    if (entry->nickname) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, entry->nickname, 
+                                          strlen(entry->nickname));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(entry->id, 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);
     }
-    
-    /* 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;
+
+    return;
+  }
+
+  status = SILC_STATUS_OK;
+  if (len > 1)
+    status = SILC_STATUS_LIST_START;
+
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+
+    if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      if (clients_count == 1) {
+       if (entry->nickname) {
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                              SILC_STATUS_ERR_NO_SUCH_NICK,
+                                              3, entry->nickname, 
+                                              strlen(entry->nickname));
+       } else {
+         SilcBuffer idp = silc_id_payload_encode(entry->id, 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 ok;
+      continue;
     }
 
-    silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
-                                       SILC_STATUS_ERR_NO_SUCH_NICK,
-                                       tmp, strlen(tmp));
-    goto out;
-  }
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
 
- ok:
-  /* XXX, works only for local server info */
+    if (clients_count > 1 && k == clients_count - 1)
+      status = SILC_STATUS_LIST_END;
 
-  /* 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);
+    if (count && k - 1 == count)
+      status = SILC_STATUS_LIST_END;
 
-  /* XXX */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    char nh[256], uh[256];
-    SilcSocketConnection hsock;
+    if (count && k - 1 > count)
+      break;
 
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username || !entry->userinfo)
+      continue;
+      
+    /* 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);
-
-    /* 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));
-    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));
+    if (!strchr(entry->username, '@')) {
+      strncat(uh, "@", 1);
+      hsock = (SilcSocketConnection)entry->connection;
+      len = strlen(hsock->hostname);
+      strncat(uh, hsock->hostname, len);
+    }
 
-  } 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);
+    channels = silc_server_get_client_channel_list(server, entry);
+      
+    SILC_PUT32_MSB(entry->mode, mode);
 
-  silc_free(id_string);
-  silc_buffer_free(packet);
-  silc_free(sp_buf);
+    if (entry->connection) {
+      SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
+    }
 
- out:
-  silc_server_command_free(cmd);
-}
+    if (channels)
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 7, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   6, channels->data,
+                                                   channels->len,
+                                                   7, mode, 4,
+                                                   8, idle, 4);
+    else
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 6, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   7, mode, 4,
+                                                   8, idle, 4);
+    
+    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);
 
-SILC_SERVER_CMD_FUNC(whowas)
-{
+    k++;
+  }
 }
 
-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;
+
+  /* Protocol dictates that we must always send the received WHOIS 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, silc_rng_get_rn16(server->rng));
+    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_destructor,
+                               silc_server_command_whois,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+
+    silc_command_set_ident(cmd->payload, old_ident);
+
+    silc_buffer_free(tmpbuf);
+    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);
+  /* 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 whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_WHOIS))
+    return 0;
+
+  /* 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 {
-    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;
+    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);
-      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;
+  
+  if (!clients) {
+    /* 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);
     }
-
-    silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_NICK,
-                                       tmp, strlen(tmp));
     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);
 
  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);
+
+  if (!ret)
+    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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 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(tmpbuf);
+      return FALSE;
+    }
+  }
+
+  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, count = 0, len;
+  SilcBuffer packet, idp;
+  SilcClientEntry entry = NULL;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char found = FALSE;
+  char nh[256], uh[256];
+
+  status = SILC_STATUS_OK;
+  if (clients_count > 1)
+    status = SILC_STATUS_LIST_START;
+
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
+
+    /* We will take only clients that are not valid anymore. They are the
+       ones that are not registered anymore but still have a ID. They
+       have disconnected us, and thus valid for WHOWAS. */
+    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      continue;
+    if (entry->id == NULL)
+      continue;
+
+    if (count && i - 1 == count)
+      break;
+
+    found = TRUE;
+
+    if (clients_count > 2)
+      status = SILC_STATUS_LIST_ITEM;
+
+    if (clients_count > 1 && i == clients_count - 1)
+      status = SILC_STATUS_LIST_END;
+
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username)
+      continue;
+      
+    /* 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*");
+    }
+      
+    if (entry->userinfo)
+      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, 
+                                            strlen(entry->userinfo));
+    else
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                            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);
+  }
+
+  if (found == FALSE && entry)
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, entry->nickname, 
+                                        strlen(entry->nickname));
+}
+
+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, silc_rng_get_rn16(server->rng));
+    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_destructor,
+                               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);
+
+  if (!ret)
+    silc_server_command_free(cmd);
+}
+
+/******************************************************************************
+
+                              IDENTIFY Functions
+
+******************************************************************************/
+
+static bool
+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. */
+
+    /* 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)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* 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)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* 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)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    if (!(*clients) && !(*servers) && !(*channels)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return FALSE;
+    }
+  } 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_data(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 FALSE;
+      }
+
+      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 {
+         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 {
+         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 {
+         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 TRUE;
+}
+
+/* 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 || entry->nickname || 
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+       !entry->router)
+      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_destructor,
+                                 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_destructor,
+                               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;
+  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;
+
+    len = 0;
+    for (i = 0; i < clients_count; i++)
+      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+       len++;
+
+    if (len == 0 && clients_count) {
+      entry = clients[0];
+      if (entry->nickname) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, entry->nickname, 
+                                            strlen(entry->nickname));
+      } else {
+       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                            2, idp->data, idp->len);
+       silc_buffer_free(idp);
+      }
+      
+      return;
+    }
+
+    if (len > 1)
+      status = SILC_STATUS_LIST_START;
+
+    for (i = 0, k = 0; i < clients_count; i++) {
+      entry = clients[i];
+      
+      if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+       if (clients_count == 1) {
+         SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                              2, idp->data, idp->len);
+         silc_buffer_free(idp);
+       }
+       continue;
+      }
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (clients_count > 1 && k == clients_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++;
+    }
+  }
+
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
+
+  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);
+      if (entry->server_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->server_name, 
+                                              strlen(entry->server_name));
+      } else {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, 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);
+      
+      k++;
+    }
+  }
+
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
+
+  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);
+      if (entry->channel_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->channel_name, 
+                                              strlen(entry->channel_name));
+      } else {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, 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);
+      
+      k++;
+    }
+  }
+}
+
+static int
+silc_server_command_identify_process(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  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;
+
+  /* Protocol dictates that we must always send the received IDENTIFY 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, silc_rng_get_rn16(server->rng));
+    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_destructor,
+                               silc_server_command_identify,
+                               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. */
+
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_identify_parse(cmd,
+                                         &clients, &clients_count,
+                                         &servers, &servers_count,
+                                         &channels, &channels_count,
+                                         &count))
+    return 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);
+
+  if (!ret)
+    silc_server_command_free(cmd);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+static int silc_server_command_bad_chars(char *nick)
+{
+  int i;
+
+  for (i = 0; i < strlen(nick); i++) {
+    if (!isascii(nick[i]))
+      return TRUE;
+    if (nick[i] <= 32) return TRUE;
+    if (nick[i] == ' ') return TRUE;
+    if (nick[i] == '\\') return TRUE;
+    if (nick[i] == '\"') return TRUE;
+    if (nick[i] == '*') return TRUE;
+    if (nick[i] == '?') return TRUE;
+    if (nick[i] == ',') return TRUE;
+    if (nick[i] == '@') return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* 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;
+  SilcClientID *new_id;
+  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, 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;
+  }
+
+  if (strlen(nick) > 128)
+    nick[128] = '\0';
+
+  /* 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, FALSE);
+
+  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 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);
+  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;
+  SilcBuffer packet, idp;
+  SilcChannelEntry entry;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char *topic;
+  unsigned char usercount[4];
+  uint32 users;
+
+  for (i = 0; i < lch_count; i++)
+    if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      lch[i] = NULL;
+  for (i = 0; i < gch_count; i++)
+    if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      gch[i] = NULL;
+
+  status = SILC_STATUS_OK;
+  if ((lch_count + gch_count) > 1)
+    status = SILC_STATUS_LIST_START;
+
+  /* Local list */
+  for (i = 0; i < lch_count; i++) {
+    entry = lch[i];
+
+    if (!entry)
+      continue;
+
+    if (i >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+
+    if (i == lch_count - 1 && gch_count)
+      break;
+    if (lch_count > 1 && i == lch_count - 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 */
+    if (topic)
+      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, strlen(topic),
+                                            5, usercount, 4);
+    else
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                            status, ident, 3, 
+                                            2, idp->data, idp->len,
+                                            3, entry->channel_name, 
+                                            strlen(entry->channel_name),
+                                            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);
+  }
+
+  status = i ? SILC_STATUS_LIST_ITEM : SILC_STATUS_OK;
+
+  /* Global list */
+  for (i = 0; i < gch_count; i++) {
+    entry = gch[i];
+
+    if (!entry)
+      continue;
+
+    if (i >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+
+    if (gch_count > 1 && i == lch_count - 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 */
+    if (topic)
+      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, strlen(topic),
+                                            5, usercount, 4);
+    else
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                            status, ident, 3, 
+                                            2, idp->data, idp->len,
+                                            3, entry->channel_name, 
+                                            strlen(entry->channel_name),
+                                            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);
+  }
+}
+
+/* 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, 2);
+
+  /* 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 if we are router */
+  if (server->server_type != SILC_SERVER) 
+    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);
+
+ 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,
+                                       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, TRUE,
+                                      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);
+  if (channel->topic)
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                                 SILC_STATUS_OK, ident, 2, 
+                                                 2, idp->data, idp->len,
+                                                 3, channel->topic, 
+                                                 strlen(channel->topic));
+  else
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                                 SILC_STATUS_OK, ident, 1, 
+                                                 2, idp->data, idp->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(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];
+
+    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);
+    if (!dest) {
+      if (server->server_type != SILC_SERVER) {
+       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_destructor,
+                                 silc_server_command_invite, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_free(channel_id);
+      silc_free(dest_id);
+      return;
+    }
+
+    /* 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;
+
+  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);
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, TRUE, NULL);
+    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 {
+    /* Remove remote client */
+    if (!silc_idlist_del_client(server->global_list, remote_client))
+      silc_idlist_del_client(server->local_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->admin_info->location,
+            server->config->admin_info->server_type,
+            server->config->admin_info->admin_name,
+            server->config->admin_info->admin_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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
+
+    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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
+  }
+
+  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 */
+  if (server_info)
+    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, 
+                                                 strlen(server_info));
+  else
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                                 SILC_STATUS_OK, ident, 2,
+                                                 2, idp->data, idp->len,
+                                                 3, server_name, 
+                                                 strlen(server_name));
+  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)
+{
+  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];
+
+  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);
+    if (!client) {
+      if (cmd->pending)
+       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, NULL,
+                                 silc_server_command_join, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      return;
+    }
+
+    cmd->pending = FALSE;
+  }
+
+  /*
+   * Check channel modes
+   */
+
+  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 (!channel->ban_list ||
+        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;
+    }
+  }
+
+  /* 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);
+  }
+  
+  /* Check the channel passphrase if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!passphrase || !channel->passphrase ||
+        memcmp(channel->passphrase, passphrase,
+               strlen(channel->passphrase))) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_BAD_PASSWORD);
+      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;
+    }
+  }
+
+  /*
+   * 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);
+  }
+
+  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;
+  uint32 tmp_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, 1, 4);
+
+  /* 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 (strlen(channel_name) > 256)
+    channel_name[255] = '\0';
+
+  if (silc_server_command_bad_chars(channel_name) == 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 and hmac name */
+  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+
+  /* 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) {
+    /* If this is coming from client the Client ID in the command packet must
+       be same as the client's ID. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+      if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+    }
+
+    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, silc_rng_get_rn16(server->rng));
+         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_destructor,
+                                     silc_server_command_join,
+                                     silc_server_command_dup(cmd));
+         cmd->pending = TRUE;
+         return;
+       }
+       
+       /* 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);
+      create_key = FALSE;      /* Router returned the key already */
+    }
+  }
+
+  /* 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);
+
+  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->motd && 
+       server->config->motd->motd_file) {
+      /* Send motd */
+      motd = silc_file_readfile(server->config->motd->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);
+      goto out;
+    } 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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_motd,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
+
+    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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_motd,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
+
+    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);
+
+    if (entry->motd)
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                   SILC_STATUS_OK, ident, 2,
+                                                   2, idp, idp->len,
+                                                   3, entry->motd,
+                                                   strlen(entry->motd));
+    else
+      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);
+  }
+
+ 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 {
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
+      /* Remove the server operator rights */
+      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+  }
+
+  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 {
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
+      /* Remove the router operator rights */
+      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+  }
+
+  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;
+  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 */
+      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 */
+
+      /* 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 */
+      silc_cipher_free(channel->channel_key);
+      if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      /* 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);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      /* Cipher mode is unset. Remove the cipher and revert back to 
+        default cipher */
+      cipher = channel->cipher;
+
+      /* Delete old cipher and allocate default one */
+      silc_cipher_free(channel->channel_key);
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, 
+                            &channel->channel_key)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      /* 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);
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+      /* HMAC to use protect the traffic */
+      unsigned char hash[32];
+
+      /* 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 */
+      silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       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));
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+      /* Hmac mode is unset. Remove the hmac and revert back to 
+        default hmac */
+      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, 
+                          &channel->hmac)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       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));
+    }
+  }
+
+  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, 4,
+                                    cidp->data, cidp->len, 
+                                    tmp_mask, 4,
+                                    cipher, cipher ? strlen(cipher) : 0,
+                                    hmac, hmac ? strlen(hmac) : 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);
+
+  /* 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_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 && chl->client != target_client) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_YOU);
+    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(sp_buf);
+  silc_free(channel_id);
+  silc_free(client_id);
+  silc_buffer_free(idp);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* 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;
+  unsigned char *tmp, *comment;
+
+  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 */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp) {
+    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(tmp, tmp_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(target_client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                    SILC_NOTIFY_TYPE_KICKED, 
+                                    comment ? 2 : 1,
+                                    idp->data, idp->len,
+                                    comment, comment ? strlen(comment) : 0);
+  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, 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);
+}
+
+/* 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;
+  SilcServerConfigSectionAdminConnection *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;
+
+  /* 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);
+}
+
+/* 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;
+  SilcServerConfigSectionAdminConnection *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;
+
+  /* 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_SILCOPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* 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;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *tmp, *host;
+  uint32 tmp_len;
+  uint32 port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
+
+  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);
-#undef LCC
-#undef LCCC
 }
 
-SILC_SERVER_CMD_FUNC(list)
-{
-}
+/* 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(topic)
+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(invite)
-{
-}
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
 
-/* Quits connection to client. This gets called if client won't
-   close the connection even when it has issued QUIT command. */
+  /* 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_TASK_CALLBACK(silc_server_command_quit_cb)
-{
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
+  /* 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;
+    }
+  }
 
-  /* 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);
+  /* 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;
+  }
 
-  /* Close the connection on our side */
-  silc_server_close_connection(server, sock);
+  /* Get entry to the channel user list */
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+
+  /* 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;
+  }
+
+  /* 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);
+  }
+
+  /* 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 */
+  if (channel->ban_list)
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
+                                          SILC_STATUS_OK, ident, 2,
+                                          2, id, id_len,
+                                          3, channel->ban_list, 
+                                          strlen(channel->ban_list) - 1);
+  else
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
+                                          SILC_STATUS_OK, ident, 1,
+                                          2, id, id_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(channel_id);
+  silc_server_command_free(cmd);
 }
 
-/* Quits SILC session. This is the normal way to disconnect client. */
+/* Server side command of CLOSE. Closes connection to a specified server. */
  
-SILC_SERVER_CMD_FUNC(quit)
+SILC_SERVER_CMD_FUNC(close)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
+  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);
 
-  /* 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;
 
-  silc_server_command_free(cmd);
-}
+  /* 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;
+  }
 
-SILC_SERVER_CMD_FUNC(kill)
-{
-}
+  /* 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;
+  }
 
-SILC_SERVER_CMD_FUNC(info)
-{
-}
+  /* 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;
+  }
 
-SILC_SERVER_CMD_FUNC(connect)
-{
-}
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                       SILC_STATUS_OK);
 
-SILC_SERVER_CMD_FUNC(ping)
-{
+  /* 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);
+  silc_server_close_connection(server, sock);
+  
+ out:
+  silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(oper)
+/* 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;
 
-typedef struct {
-  char *channel_name;
-  char *nickname;
-  char *username;
-  char *hostname;
-  SilcChannelList *channel;
-  SilcServer server;
-} JoinInternalContext;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
 
-SILC_TASK_CALLBACK(silc_server_command_join_notify)
-{
-  JoinInternalContext *ctx = (JoinInternalContext *)context;
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    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);
+  /* 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;
   }
-}
 
-/* Server side of command JOIN. Joins client into requested channel. If 
-   the channel does not exist it will be created. */
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
+                                       SILC_STATUS_OK);
 
-SILC_SERVER_CMD_FUNC(join)
+  /* 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;
-  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;
-
-  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]
-
-  /* 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);
+  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;
   }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+  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;
   }
 
-  /* 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);
-    goto out;
+  /* 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;
+    }
   }
-  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);
+  /* 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;
   }
-  
-  /* Get cipher name */
-  cipher = silc_command_get_arg_type(cmd->payload, 3, NULL);
 
-  /* 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;
-    }
-  }
+  /* 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);
 
-  /* 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) {
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                       SILC_STATUS_OK);
 
-    /* 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
-    }
+  /* 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;
+
+  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 */
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
   }
 
-  channel = (SilcChannelList *)id_cache->context;
+ out:
+  silc_free(id);
+  silc_server_command_free(cmd);
+}
 
- join_channel:
+/* 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. */
 
-  /* XXX must check whether the client already is on the channel */
+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;
 
-  /* 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;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
 
-  /* 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);
-  }
-  channel->user_list_count++;
+  /* Get Channel ID */
+  channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
 
-  i = client->channel_count;
-  client->channel = silc_realloc(client->channel, 
-                                sizeof(*client->channel) * (i + 1));
-  client->channel[i] = channel;
-  client->channel_count++;
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
 
-  /* 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) {
+  if (!channel_id && !channel_name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
 
+  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;
+    }
   }
 
-  /* 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);
+  /* 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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 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);
-    } 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);
-  }
+      return;
+    }
 
-  /* 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);
+    /* 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 (id_string)
-    silc_free(id_string);
-
-  /* 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);
+  /* 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;
 
   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_data(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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_getkey,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
 
-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 */
+    if (!client->data.public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      tmp = silc_pkcs_public_key_encode(client->data.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, silc_rng_get_rn16(server->rng));
+      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_destructor,
+                                 silc_server_command_getkey,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      return;
+    }
 
-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 */
+    if (!server_entry->data.public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      tmp = silc_pkcs_public_key_encode(server_entry->data.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..da7a3ea737b96775ae255265838a5e3acce104a7 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,32 @@ 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;
 
+/* Pending Command callback destructor. This is called after calling the
+   pending callback or if error occurs while processing the pending command.
+   If error occurs then the callback won't be called at all, and only this
+   destructor is called. The `context' is the context given for the function
+   silc_server_command_pending. */
+typedef void (*SilcServerPendingDestructor)(void *context);
+
 /* 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;
-
+  SilcServerPendingDestructor destructor;
+  void *context;
+  uint16 ident;
   struct SilcServerCommandPendingStruct *next;
 } SilcServerCommandPending;
 
-/* List of pending commands */
-extern SilcServerCommandPending *silc_command_pending;
+#include "command_reply.h"
 
 /* Macros */
 
@@ -78,60 +85,58 @@ 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;                                          \
-      }                                                        \
-    }                                                  \
-  }                                                    \
+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); \
 } 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);              \
-  }                                                    \
+/* Execute destructor for pending command */
+#define SILC_SERVER_PENDING_DESTRUCTOR(ctx, cmd)                          \
+do {                                                                      \
+  int _i;                                                                 \
+  silc_server_command_pending_del(ctx->server, cmd, ctx->ident);          \
+  for (_i = 0; _i < ctx->callbacks_count; _i++)                                   \
+    if (ctx->callbacks[_i].destructor)                                    \
+      (*ctx->callbacks[_i].destructor)(ctx->callbacks[_i].context);       \
 } 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);
+void silc_server_command_pending(SilcServer server,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcServerPendingDestructor destructor,
                                 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 +157,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..cb6c6973793d99e1480b6b5f09b61e7c27232a36 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),
 
   { NULL, 0 },
 };
@@ -46,11 +67,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);
   if (!payload) {
     /* Silently ignore bad reply packet */
     SILC_LOG_DEBUG(("Bad command reply packet"));
@@ -61,14 +87,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 +117,580 @@ 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;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  char global = FALSE;
+  char *nick;
+  uint32 mode = 0, len, id_len;
+
+  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;
+
+  /* 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);
+    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;
+
+    /* Create new cache entry */
+    silc_idcache_add(global ? server->global_list->clients :
+                    server->local_list->clients, nick, client->id, 
+                    client, FALSE);
+    silc_free(client_id);
+  }
+
+  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;
+  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:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+  SILC_SERVER_PENDING_DESTRUCTOR(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);
+    if (!client) {
+      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+      return FALSE;
+    }
+
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, &cache);
+    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+    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, FALSE);
+  }
+
+  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_PENDING_DESTRUCTOR(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;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
+  if (!id_data)
+    return FALSE;
+  idp = silc_id_payload_parse_data(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);
+      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) {
+       /* Add new cache entry */
+       silc_idcache_add(global ? server->global_list->clients :
+                        server->local_list->clients, nick, client->id, 
+                        client, FALSE);
+      }
+
+      silc_free(client_id);
+    }
+
+    break;
+
+  case SILC_ID_SERVER:
+    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:
+    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_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_id(server->global_list, channel_id,
+                                              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->connection, 
+                                       NULL, NULL);
+      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;
+  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:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
+  SILC_SERVER_PENDING_DESTRUCTOR(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_PENDING_DESTRUCTOR(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_PENDING_DESTRUCTOR(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 +699,379 @@ 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;
 
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK)
+  /* Get channel ID */
+  id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
+  if (!id_string)
     goto out;
 
-  /* Get channel name */
-  tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
+  /* 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;
+
+  /* Get mode mask */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
+
+  /* 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);
+  }
+
+  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)
+      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);
+    if (!entry) {
+      silc_free(id);
+      goto out;
+    }
+    server->stat.my_channels++;
+  } else {
+    /* The entry exists. */
+    silc_free(cache->id);
+    entry->id = id;
+    cache->id = entry->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);
+      if (entry->founder_passwd) {
+       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) {
+    if (entry->ban_list)
+      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) {
+    if (entry->invite_list)
+      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) {
+    if (entry->topic)
+      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 (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
+    silc_server_save_channel_key(server, keyp, entry);
+  if (keyp)
+    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_SERVER_PENDING_DESTRUCTOR(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;
+
+  /* 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,
+                                 NULL, 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);
 
-  channel_name = strdup(tmp);
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  if (!tmp)
+    goto out;
 
-  /* 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;
+  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);
 
-  /* 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);
+  /* 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_SERVER_PENDING_DESTRUCTOR(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;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+  idp = silc_id_payload_parse_data(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_USERS);
+  SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
+  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);
-#undef LCC
-#undef LCCC
 }
index 0e67aa287dbb2dddb0119ca1fe7df8f42d28c6e2..eb5b53d48553b81a5a6718387e3469aa0c9941bb 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,24 @@ typedef struct {
 /* All server command replys */
 extern SilcServerCommandReply silc_command_reply_list[];
 
+/* Context holding pending command callbacks. */
+typedef struct {
+  SilcServerPendingDestructor destructor;
+  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 +60,20 @@ 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);
 
 #endif
index 5f04a26fd691c81ce893bf4a0d34d1128d4b8de2..4a72bcb9f95ce557bbb25a0131e990b9c4b538d3 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;
+  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->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, 600, 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, FALSE)) {
+    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(("Server by name `%s'", name));
+
+  if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
+    return NULL;
 
-  SILC_LOG_DEBUG(("Finding client by nickname"));
+  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;
+}
 
-  if (!list)
+/* 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;
 
-  first = entry = list;
-  if (!strcmp(entry->nickname, nickname)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
+  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);
 
-  return NULL;
+  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_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;
+
+  if (!old_id || !new_id)
+    return NULL;
 
-  SILC_LOG_DEBUG(("Finding client by nickname hash"));
+  SILC_LOG_DEBUG(("Replacing Server ID"));
 
-  if (!list)
+  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, FALSE);
 
-  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)
+{
+  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, FALSE)) {
+    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"));
+
+  if (entry) {
+    /* Remove from cache */
+    if (entry->id)
+      if (!silc_idcache_del_by_context(id_list->clients, entry))
+       return FALSE;
 
-  SILC_LOG_DEBUG(("Adding new channel to id list"));
+    /* Free data */
+    silc_free(entry->nickname);
+    silc_free(entry->username);
+    silc_free(entry->userinfo);
+    silc_free(entry->id);
 
-  idlist = silc_calloc(1, sizeof(*idlist));
-  if (idlist == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new channel list object"));
-    return;
-  }
+    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;
+
+  *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;
+}
+
+/* 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)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  unsigned char hash[32];
+  SilcClientID client_id;
+
+  SILC_LOG_DEBUG(("Start"));
 
-  if (new_idlist)
-    *new_idlist = idlist;
+  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;
 }
 
-SilcChannelList *
-silc_idlist_find_channel_by_id(SilcChannelList *list, SilcChannelID *id)
+/* Finds client by Client ID */
+
+SilcClientEntry
+silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
+                             bool registered, SilcIDCacheEntry *ret_entry)
 {
-  SilcChannelList *first, *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;
+
+  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(("Finding channel by Channel ID"));
+  SILC_LOG_DEBUG(("Replacing Client ID"));
 
-  if (!list)
+  /* 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;
 
-  first = entry = list;
-  if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
+  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, FALSE);
+
+  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;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  client = (SilcClientEntry)entry->context;
+  if (client) {
+    if (client->nickname)
+      silc_free(client->nickname);
+    if (client->username)
+      silc_free(client->username);
+    if (client->userinfo)
+      silc_free(client->userinfo);
+    if (client->id)
+      silc_free(client->id);
+
+    memset(client, 'F', sizeof(*client));
+    silc_free(client);
   }
-  entry = entry->next;
+}
+
+/******************************************************************************
 
-  while(entry != first) {
-    if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) {
-      SILC_LOG_DEBUG(("Found"));
-      return entry;
+                          Channel entry functions
+
+******************************************************************************/
+
+/* 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)
+{
+  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, FALSE)) {
+    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;
+
+  /* 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 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);
+    silc_free(entry->hmac_name);
+    silc_free(entry->rekey);
+
+    /* 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);
+
+    memset(entry, 'F', sizeof(*entry));
+    silc_free(entry);
+    return TRUE;
+  }
 
-    if (entry->user_list_count)
-      silc_free(entry->user_list);
+  return FALSE;
+}
 
-    /* Last one in list? */
-    if (*list == entry && entry->next == entry) {
-      *list = NULL;
-      silc_free(entry);
-      return;
-    }
+/* Finds channel by channel name. Channel names are unique and they
+   are not case-sensitive. */
 
-    /* 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;
-    }
+SilcChannelEntry
+silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
+                                SilcIDCacheEntry *ret_entry)
+{
+  SilcIDCacheEntry id_cache = NULL;
 
-    /* Remove from list */
-    entry->prev->next = entry->next;
-    entry->next->prev = entry->prev;
-    silc_free(entry);
-    return;
+  SILC_LOG_DEBUG(("Channel by 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, FALSE);
+
+  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..0436d9a4ed2481969e2cfa6a527e0b0561ef7b21 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;
+} *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;
+
+  /* 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 +124,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 +144,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 +169,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 +232,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 +282,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 +318,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 +377,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 +401,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 +499,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 +522,96 @@ 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 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 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..099a455
--- /dev/null
@@ -0,0 +1,2508 @@
+/*
+
+  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;
+
+  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);
+  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);
+       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;
+
+    /* 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;
+
+    /* 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;
+    server->stat.clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    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;
+    }
+
+    if (channel->topic)
+      silc_free(channel->topic);
+    channel->topic = silc_calloc(tmp_len + 1, sizeof(*channel->topic));
+    memcpy(channel->topic, tmp, tmp_len);
+
+    /* 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));
+    }
+
+    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);
+           goto out;
+         }
+       }
+       
+       if (chl->client == client) {
+         if (chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
+         /* Change the mode */
+         chl->mode = mode;
+         if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+           break;
+         
+         chl2 = chl;
+       }
+      }
+      
+      /* 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->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;
+      }
+    }
+
+    /* 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->global_list, channel_id,
+                                       channel_id2))
+      if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+                                         channel_id2)) {
+       silc_free(channel_id2);
+       channel_id2 = NULL;
+      }
+
+    if (channel_id2) {
+      SilcBuffer users = NULL, users_modes = NULL;
+      
+      /* 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);
+      }
+    }
+
+    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);
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, TRUE, NULL);
+      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);
+           if (!client) {
+             client = silc_idlist_find_client_by_id(server->local_list, 
+                                                    client_id, TRUE, &cache);
+             if (!client) {
+               silc_free(client_id);
+               continue;
+             }
+           }
+           silc_free(client_id);
+
+           /* Remove the client from all channels. */
+           silc_server_remove_from_channels(server, NULL, client, 
+                                            TRUE, NULL, FALSE);
+           
+           client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+           cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+           server->stat.clients--;
+           if (server->server_type == SILC_ROUTER)
+             server->stat.cell_clients--;
+         }
+       }
+
+       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 */
+    if (!silc_idlist_del_server(server->global_list, server_entry))
+      silc_idlist_del_server(server->local_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;
+
+    /* Save the mode */
+    SILC_GET32_MSB(client->mode, tmp);
+
+    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 (!strcmp(channel->ban_list, tmp)) {
+       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)
+    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;
+
+  /* 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, packet->dst_id_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;
+  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_STRING_ALLOC(&username),
+                            SILC_STR_UI16_STRING_ALLOC(&realname),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (username)
+      silc_free(username);
+    if (realname)
+      silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (!username) {
+    silc_free(username);
+    if (realname)
+      silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (strlen(username) > 128)
+    username[127] = '\0';
+
+  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);
+      if (realname)
+       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);
+      if (phostname)
+       silc_free(phostname);
+      if (realname)
+       silc_free(realname);
+      silc_server_disconnect_remote(server, sock, 
+                                   "Server closed connection: "
+                                   "Incomplete client information");
+      return NULL;
+    }
+    
+    if (phostname)
+      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, FALSE);
+
+  /* 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,
+                           ("%d server operators and %d router operators "
+                            "online",
+                            server->stat.my_server_ops,
+                            server->stat.my_router_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, FALSE);
+
+  /* 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);
+  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);
+    if (!router)
+      goto out;
+    router_sock = sock;
+    id_list = server->global_list;
+  }
+
+  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);
+      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);
+  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);
+      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 */
+      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);
+      }
+
+      /* 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_CHANNEL_LEN;
+       
+       /* 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)
+{
+  SilcServerConfigSectionClientConnection *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_conn(server->config,
+                                              sock->ip,
+                                              port);
+  if (!client)
+    client = silc_server_config_find_client_conn(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..acea264
--- /dev/null
@@ -0,0 +1,1820 @@
+/*
+
+  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;
+  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. */
+
+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 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, 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_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_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);
+    }
+  }
+}
+
+/* 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;
+  uint32 motd_len;
+
+  if (server->config && server->config->motd && 
+      server->config->motd->motd_file) {
+
+    motd = silc_file_readfile(server->config->motd->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)
+{
+  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,
+                              4, idp->data, idp->len,
+                              mode, 4,
+                              cipher, cipher ? strlen(cipher) : 0,
+                              hmac, hmac ? strlen(hmac) : 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,
+                                    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 `client_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,
+                                      SilcClientID *client_id,
+                                      char *topic)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify(server, sock, broadcast,
+                         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,
+                                   char *comment)
+{
+  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_KICKED,
+                              comment ? 2 : 1, idp->data, idp->len,
+                              comment, comment ? strlen(comment) : 0);
+  silc_buffer_free(idp);
+}
+
+/* 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,
+                                       unsigned char 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_free(routed);
+  silc_free(sent_clients);
+  silc_free(packetdata.src_id);
+  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. Normal server nevers sends this packet. Router uses this
+   to notify other routers in the network about new channel. This packet
+   is broadcasted. */
+
+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->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(("Start"));
+  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);
+}
+
+/* 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..48e1297
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+
+  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);
+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,
+                                    char *message);
+void silc_server_send_notify_topic_set(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      bool broadcast,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *client_id,
+                                      char *topic);
+void silc_server_send_notify_kicked(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   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,
+                                       unsigned char 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_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..c78ff8e26a66992289613673aa1cd6fe2f6b85b7 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;
+  }
+
+  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(encpk, pk, 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 +199,218 @@ 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(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);
+  }
+
+  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;
   }
 
-  /* 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, &conn_data->pkcs);
-  silc_pkcs_set_public_key(conn_data->pkcs, 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, &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);
+  /* 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);
 
   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 (min > min2)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* XXX < 0.6 is not allowed */
+  if (maj == 0 && min < 5)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  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 +423,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 +443,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, FALSE);
       } 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_NONE, 
+                                             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 +498,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 +537,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 +572,118 @@ 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);
 
       /* 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_schedule_task_del(server->schedule, ctx->timeout_task);
+
+    /* 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
+     */
+
     /* 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);
 
     /* 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_UNKNOWN:
     break;
   }
@@ -397,6 +693,96 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
  * Connection Authentication protocol functions
  */
 
+static int 
+silc_server_password_authentication(SilcServer server, char *auth1, 
+                                   char *auth2)
+{
+  if (!auth1 || !auth2)
+    return FALSE;
+
+  if (!memcmp(auth1, auth2, strlen(auth1)))
+    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 +812,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 +845,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 +875,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);
+         SilcServerConfigSectionClientConnection *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_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);
+         SilcServerConfigSectionServerConnection *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_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);
-         
+         SilcServerConfigSectionServerConnection *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_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 +1078,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 +1141,23 @@ 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);
 
       /* 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 +1165,476 @@ 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);
 
       /* 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);
+
+    /* 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_get_group_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_get_group_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);
+
+    /* 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);
+    }
+
+    /* 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
+     */
+
+    /* 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..21c3d193bd987bf00aa024713eae8b88e2a6e439 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,50 @@ 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(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..31ae88774e476c065cd8b322172ff6f0be89bfdb 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);
   }
 }
@@ -121,28 +111,54 @@ void silc_server_free(SilcServer server)
 
 int silc_server_init(SilcServer server)
 {
-  int *sock = NULL, sock_count, i;
+  int *sock = NULL, sock_count = 0, i;
   SilcServerID *id;
-  SilcServerList *id_entry;
-  SilcHashObject hash;
+  SilcServerEntry id_entry;
+  SilcIDListPurge purge;
+  SilcServerConfigSectionListenPort *listen;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
   assert(server->config);
 
+  /* Set public and private keys */
+  if (!server->config->server_keys ||
+      !server->config->server_keys->public_key || 
+      !server->config->server_keys->private_key) {
+    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
+    return FALSE;
+  }
+  server->public_key = server->config->server_keys->public_key;
+  server->private_key = server->config->server_keys->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;
+
   /* Set log files where log message should be saved. */
   server->config->server = server;
-  silc_config_server_setlogfiles(server->config);
+  silc_server_config_setlogfiles(server->config);
  
   /* 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->config))
+    silc_cipher_register_default();
+  if (!silc_server_config_register_pkcs(server->config))
+    silc_pkcs_register_default();
+  if (!silc_server_config_register_hashfuncs(server->config))
+    silc_hash_register_default();
+  if (!silc_server_config_register_hmacs(server->config))
+    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);
@@ -151,61 +167,47 @@ int silc_server_init(SilcServer server)
   /* 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);
+  /* 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);
 
-    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);
-
-    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) {
+  listen = server->config->listen_port;
+  while(listen) {
     int tmp;
 
     tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->host);
-    if (tmp < 0)
+                                server->config->listen_port->listener_ip);
+    if (tmp < 0) {
+      SILC_LOG_ERROR(("Could not create server listener: %s on %d",
+                     server->config->listen_port->listener_ip,
+                     server->config->listen_port->port));
       goto err0;
+    }
 
     sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
     sock[sock_count] = tmp;
-    server->config->listen_port = server->config->listen_port->next;
     sock_count++;
+    listen = listen->next;
   }
 
+  /* 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 
@@ -227,6 +229,8 @@ int silc_server_init(SilcServer server)
     }
     
     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,22 +238,39 @@ 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;
+    
+    /* 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]);
 
     /* Put the allocated socket pointer also to the entry allocated above 
        for fast back-referencing to the socket list. */
@@ -257,54 +278,77 @@ int silc_server_init(SilcServer server)
     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) {
-    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;
-  }
+  /* Register protocols */
+  silc_server_protocols_register();
 
-  /* Initialize the scheduler */
-  silc_schedule_init(server->io_queue, server->timeout_queue, 
-                    server->generic_queue, 
-                    SILC_SERVER_MAX_CONNECTIONS);
+  /* Initialize the scheduler. */
+  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
+  if (!server->schedule)
+    goto err0;
   
-  /* 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;
 
   /* 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) {
+    SilcServerConfigSectionServerConnection *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;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 600, 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;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 300, 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 +356,125 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Fork server to background and set gid+uid to non-root.
+   Silcd will not run as root, so trying to set either user or group to
+   root will cause silcd to exit. */
+
+void silc_server_daemonise(SilcServer server)
+{
+  /* Are we executing silcd as root or a regular user? */
+  if (geteuid()==0) {
+    
+    struct passwd *pw;
+    struct group *gr;
+    char *user, *group;
+    
+    if (!server->config->identity || !server->config->identity->user || 
+       !server->config->identity->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->identity->user;
+    group=server->config->identity->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);
+    }
+    
+    pw=getpwnam(user);
+    gr=getgrnam(group);
+
+    if (!pw) {
+      fprintf(stderr, "No such user %s found\n", user);
+      exit(1);
+    }
+
+    if (!gr) {
+      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 {
+      /* Fork server to background, making it a daemon */
+      if (fork()) {
+        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+        SILC_LOG_DEBUG(("Forking SILC server to background"));
+        exit(0);
+      } 
+      setsid();
+      
+      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);
+      }
+      SILC_LOG_DEBUG(("Changing to user nobody"));
+      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);
+      }
+    }
+  } else {
+    /* Fork server to background, making it a daemon */
+    if (fork()) {
+      SILC_LOG_DEBUG(("Server started as user")); 
+      SILC_LOG_DEBUG(("Forking SILC server to background"));
+      exit(0);
+    }
+    setsid();
+  }
+}
+
+/* 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 +483,150 @@ 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();
+  silc_schedule_stop(server->schedule);
+  silc_schedule_uninit(server->schedule);
+
+  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();
+  /* 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"));
+    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->listen_port->local_ip,
+                                   sconn->remote_port, 
+                                   sconn->remote_host);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not connect to router"));
+    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 +635,50 @@ void silc_server_run(SilcServer server)
 SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
-  int sock;
+  SilcServerConnection sconn;
+  SilcServerConfigSectionServerConnection *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;
-
-      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 +686,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 +697,103 @@ 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;
+  SilcServerConfigSectionServerConnection *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(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 +809,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 +828,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 +879,268 @@ 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, 600, 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);
+  }
 
-  /* Free the temporary connection data context from key exchange */
-  silc_free(conn_data);
+  sock->protocol = NULL;
 
-  /* Free the protocol object */
+  /* 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);
+  }
+
+  /* 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);
-  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;
+  SilcServerConfigSectionDenyConnection *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_denied_conn(server->config, sock->ip, port);
+  if (!deny)
+    deny = silc_server_config_denied_conn(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->comment ?
+                                 deny->comment :
+                                 "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_conn(server->config,
+                                                     sock->ip, port)))
+    cconfig = silc_server_config_find_client_conn(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_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 +1155,76 @@ 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(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 +1240,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 +1258,188 @@ 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 = 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;
     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);
+      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;
+      SilcServerConfigSectionServerConnection *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:
     break;
   }
 
+  sock->type = ctx->conn_type;
+
+  /* Add the common data structure to the ID entry. */
+  if (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, 600, 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 +1447,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,556 +1500,236 @@ 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]", 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;
+  }    
 
+  /* 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);
       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;
-         }
+    SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+    SILC_SET_DISCONNECTING(sock);
 
-         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_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock);
+    silc_server_close_connection(server, sock);
+    return;
+  }
 
-         /* 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);
+  /* 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;
+  }
 
-         /* 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;
+  server->stat.packets_received++;
 
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
+  /* 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. */
+  silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                             TRUE : FALSE, cipher, hmac, sequence, 
+                             silc_server_packet_parse, server);
+}
+  
+/* Parses whole packet, received earlier. */
 
-       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;
-         }
+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;
 
-         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;
-         }
+  SILC_LOG_DEBUG(("Start"));
 
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
+  /* 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_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);
+  /* 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;
+  }
 
-         /* 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 {
+  if (ret == SILC_PACKET_NONE)
+    goto out;
 
-       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;
+  /* 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, packet->dst_id_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)
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
+                             void *context)
 {
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned int mac_len = 0;
+  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 (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;
+  }
 
-  switch(sock->type) {
+  switch (sock->type) {
+  case SILC_SOCKET_TYPE_UNKNOWN:
   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;
-    }
+    /* 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) {
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-    }
+    /* Packets from servers are parsed immediately */
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
     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;
-    }
+    return TRUE;
   }
 
-  /* Check MAC */
-  if (hmac) {
-    int headlen = buffer->data - buffer->head;
-    unsigned char *packet_mac, mac[32];
-    
-    SILC_LOG_DEBUG(("Verifying MAC"));
+  return TRUE;
+}
 
-    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)
-{
-  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);
-  }
-
-  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) {
-  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 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;
-}
-
-/* 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. */
+/* Parses the packet type and calls what ever routines the packet type
+   requires. This is done for all incoming packets. */
 
 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 +1737,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 +1808,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 +1817,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 +1848,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 +1868,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 +1933,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 +1976,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 +1995,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 +2022,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 +2035,1836 @@ 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. */
-
-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);
-
-    silc_buffer_clear(sock->outbuf);
-
-    if (ret == -1)
-      SILC_LOG_ERROR(("Could not write, packet dropped"));
-    if (ret != -2) {
-      silc_buffer_clear(sock->outbuf);
-      return ret;
-    }
 
-    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }  
-
-  SILC_LOG_DEBUG(("Packet in queue"));
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    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);
+  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;
 
-  /* 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);
+  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;
 
-  return 0;
-}
+  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;
 
-/* Prepare outgoing data buffer for packet sending. This is internal
-   routine and must always be called before sending any packets out. */
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 
-static void silc_server_packet_send_prepare(SilcServer server, 
-                                           SilcSocketConnection sock,
-                                           unsigned int header_len,
-                                           unsigned int padlen,
-                                           unsigned int data_len)
-{
-  int totlen, oldlen;
+      SilcServerRekeyInternalContext *proto_ctx = 
+       (SilcServerRekeyInternalContext *)sock->protocol->context;
 
-  totlen = header_len + padlen + data_len;
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
 
-  /* 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. */
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
-      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);
+      /* Let the protocol handle the packet */
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } 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);
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
     }
-  }
-}
-
-/* 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;
+    break;
 
-  /* 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;
-    }
+  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;
-  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;
-    }
+
+  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;
+
   default:
+    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     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, 
-                                 unsigned int data_len,
-                                 int force_send)
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+                                  char *remote_host, uint32 port)
 {
-  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;
+  SilcServerConnection sconn;
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+  /* 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;
 
-  /* 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;
-  }
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_connect_router,
+                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
+}
 
-  if (dst_id) {
-    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
-    dst_id_len = silc_id_get_len(dst_id_type);
-  }
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
+{
+  silc_socket_free((SilcSocketConnection)context);
+}
 
-  /* 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;
+/* Closes connection to socket connection */
 
-  /* 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);
+void silc_server_close_connection(SilcServer server,
+                                 SilcSocketConnection sock)
+{
+  if (!server->sockets[sock->sock])
+    return;
 
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", 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")));
 
-  packetdata.buffer = sock->outbuf;
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+  /* Unregister all tasks */
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+  server->sockets[sock->sock] = NULL;
 
-  /* 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));
+  /* 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;
+    }
   }
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_close_connection_final,
+                        (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
+}
 
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
-
-  SILC_LOG_HEXDUMP(("Outgoing 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);
-
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
-}
+/* Sends disconnect message to remote connection and disconnects the 
+   connection. */
 
-/* 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)
+void silc_server_disconnect_remote(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  const char *fmt, ...)
 {
-  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;
-
-  SILC_LOG_DEBUG(("Forwarding packet"));
+  va_list ap;
+  unsigned char buf[4096];
 
-  /* 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:
-    /* We won't forward to unknown destination - keys must exist with
-       the destination before forwarding. */
+  if (!sock)
     return;
-  }
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
 
-  /* Mungle the packet flags and add the FORWARDED flag */
-  if (data)
-    data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
+  memset(buf, 0, sizeof(buf));
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  va_end(ap);
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+  SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  /* 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));
-  }
+  /* 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);
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+  /* Mark the connection to be disconnected */
+  SILC_SET_DISCONNECTED(sock);
+  silc_server_close_connection(server, sock);
+}
 
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
+typedef struct {
+  SilcServer server;
+  SilcClientEntry client;
+} *FreeClientInternal;
 
-  SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+  FreeClientInternal i = (FreeClientInternal)context;
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+  silc_idlist_del_data(i->client);
+  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+  silc_free(i);
 }
 
-/* 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. */
+/* Frees client data and notifies about client's signoff. */
 
-void silc_server_packet_send_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int force_send)
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 char *signoff)
 {
-  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);
-
-    /* Now actually send the packet */
-    silc_server_packet_send_real(server, sock, force_send);
-  }
-
-  /* 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;
+  FreeClientInternal i = silc_calloc(1, sizeof(*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. */
-    if (client && client->router && server->server_type == SILC_ROUTER) {
-      int k;
+  /* 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);
 
-      /* Check if we have sent the packet to this route already */
-      for (k = 0; k < routed_count; k++)
-       if (routed[k] == client->router)
-         continue;
+  if (!client->id)
+    return;
 
-      /* 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);
-      
-      /* 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);
+  /* 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, signoff, TRUE);
+  else
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    FALSE, NULL, FALSE);
+    
+  /* 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;
+
+  /* Free the client entry and everything in it */
+  server->stat.my_clients--;
+  server->stat.clients--;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_clients--;
+}
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
+/* Frees user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
+   entities. */
 
-      /* 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++;
+void silc_server_free_sock_user_data(SilcServer server, 
+                                    SilcSocketConnection sock)
+{
+  SILC_LOG_DEBUG(("Start"));
 
-      continue;
+  switch (sock->type) {
+  case SILC_SOCKET_TYPE_CLIENT:
+    {
+      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
+      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
+      break;
     }
+  case SILC_SOCKET_TYPE_SERVER:
+  case SILC_SOCKET_TYPE_ROUTER:
+    {
+      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);
+         }
 
-    /* Send to locally connected client */
-    if (client) {
+         /* 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);
+      }
 
-      /* XXX Check client's mode on the channel. */
+      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);
+      } 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);
+      }
 
-      /* 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);
+      /* 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;
+    }
+  default:
+    {
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
     }
   }
 
-  if (routed_count)
-    silc_free(routed);
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-  silc_buffer_free(payload);
+  /* 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;
+  }
+
+  sock->user_data = NULL;
 }
 
-/* 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)
+/* 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. */
+
+void silc_server_remove_from_channels(SilcServer server, 
+                                     SilcSocketConnection sock,
+                                     SilcClientEntry client,
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen)
 {
-  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);
-    }
-  }
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer clidp;
 
-  /* 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(("Start"));
 
-    if (client) {
+  if (!client || !client->id)
+    return;
 
-      /* 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;
-      }
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-      /* 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;
+  /* 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;
+    }
 
-       /* 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;
+    /* 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);
        }
-
-       /* 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. */
 
+      /* 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;
+    }
 
-      /* 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);
+    /* 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))
+       return;
       
-      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);
+      /* 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);
     }
   }
 
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
+  silc_buffer_free(clidp);
 }
 
-/* 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. */
+/* 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. */
 
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
+int silc_server_remove_from_one_channel(SilcServer server, 
+                                       SilcSocketConnection sock,
+                                       SilcChannelEntry channel,
+                                       SilcClientEntry client,
+                                       int notify)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientList *client;
-  SilcClientID *id;
-  SilcSocketConnection dst_sock;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
+  SilcChannelClientEntry chl;
+  SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* 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;
-
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
-    goto out;
+  /* 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;
+  }
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
+  /* 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);
 
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+    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);
+      }
+      return FALSE;
+    }
 
-  /* 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;
+    /* 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;
   }
 
-  /* Relay the packet to the client */
-  if (client->hmac)
-    mac_len = client->hmac->hash->hash->hash_len;
+  /* 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);
 
-  dst_sock = (SilcSocketConnection)client->connection;
+  silc_buffer_free(clidp);
+  return TRUE;
+}
 
-  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));
+/* 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)
+{
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection sock = server->sockets[fd];
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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) {
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    return;
   }
-    
-  /* 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);
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock);
 
- out:
-  silc_buffer_free(buffer);
+  silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                               "Connection timeout");
 }
 
-/* Closes connection to socket connection */
-
-void silc_server_close_connection(SilcServer server,
-                                 SilcSocketConnection sock)
+/* 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)
 {
+  SilcChannelID *channel_id;
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
 
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+  SILC_LOG_DEBUG(("Creating new channel"));
 
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
-  /* Unregister all tasks */
-  silc_task_unregister_by_fd(server->io_queue, sock->sock);
-  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-  server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
-}
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
+  }
 
-/* Sends disconnect message to remote connection and disconnects the 
-   connection. */
+  channel_name = strdup(channel_name);
 
-void silc_server_disconnect_remote(SilcServer server,
-                                  SilcSocketConnection sock,
-                                  const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
+  /* Create the channel */
+  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;
+  }
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key, newhmac);
+  if (!entry) {
+    silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    return NULL;
+  }
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  entry->cipher = strdup(cipher);
+  entry->hmac_name = strdup(hmac);
+
+  /* Now create the actual key material */
+  if (!silc_server_create_channel_key(server, entry, 
+                                     silc_cipher_get_key_len(key) / 8)) {
+    silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    silc_free(entry->cipher);
+    silc_free(entry->hmac_name);
+    return NULL;
+  }
 
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
+  /* 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);
 
-  /* 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);
+  server->stat.my_channels++;
 
-  /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
-  silc_server_close_connection(server, sock);
+  return entry;
 }
 
-/* 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. */
+/* Same as above but creates the channel with Channel ID `channel_id. */
 
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
+SilcChannelEntry 
+silc_server_create_new_channel_with_id(SilcServer server, 
+                                      char *cipher, 
+                                      char *hmac,
+                                      char *channel_name,
+                                      SilcChannelID *channel_id,
+                                      int broadcast)
 {
-  SILC_LOG_DEBUG(("Start"));
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
 
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+  SILC_LOG_DEBUG(("Creating new channel"));
 
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientList *user_data = (SilcClientList *)sock->user_data;
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
-      /* Remove client from all channels */
-      silc_server_remove_from_channels(server, sock, user_data);
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
-      /* 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);
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
+  }
 
-      /* 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;
-    }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
+  channel_name = strdup(channel_name);
 
-      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);
-      }
-      silc_free(user_data);
-      break;
-    }
+  /* Create the channel */
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key, newhmac);
+  if (!entry) {
+    silc_free(channel_name);
+    return NULL;
   }
 
-  sock->user_data = NULL;
-#undef LCC
-#undef LCCC
-}
+  /* Now create the actual key material */
+  if (!silc_server_create_channel_key(server, entry, 
+                                     silc_cipher_get_key_len(key) / 8)) {
+    silc_free(channel_name);
+    return NULL;
+  }
 
-/* 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. */
+  /* 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);
 
-void silc_server_remove_from_channels(SilcServer server, 
-                                     SilcSocketConnection sock,
-                                     SilcClientList *client)
-{
-  int i, k;
-  SilcChannelList *channel;
+  server->stat.my_channels++;
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  return entry;
+}
 
-  /* 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;
+/* Channel's key re-key timeout callback. */
 
-    /* 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;
-       }
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
+{
+  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
+  SilcServer server = (SilcServer)rekey->context;
 
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+  rekey->task = NULL;
 
-       /* 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);
-      }
-    }
-  }
+  if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
+    return;
 
-  if (client->channel_count)
-    silc_free(client->channel);
-  client->channel = NULL;
-#undef LCC
-#undef LCCC
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
 }
 
-/* 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. */
+/* 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. */
 
-SILC_TASK_CALLBACK(silc_server_timeout_remote)
+bool silc_server_create_channel_key(SilcServer server, 
+                                   SilcChannelEntry channel,
+                                   uint32 key_len)
 {
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
+  int i;
+  unsigned char channel_key[32], hash[32];
+  uint32 len;
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
-                               "Connection timeout");
-}
+  SILC_LOG_DEBUG(("Generating channel key"));
 
-/* 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)
-{
-  SilcBuffer buffer = packet->buffer;
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+    return TRUE;
+  }
 
-  /* 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);
+  if (!channel->channel_key)
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key))
+      return 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);
+  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);
   }
-}
 
-/* 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;
+  /* 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));
 
-  /* Re-encrypt packet if needed */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-    unsigned char mac[32];
-    unsigned int mac_len = 0;
+  /* 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 (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));
-    }
-    
-    /* 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);
+  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;
 }
 
-/* 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). */
+/* 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_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientID *id;
-  SilcServerList *router;
-  SilcSocketConnection dst_sock;
-  SilcClientList *client;
+  SilcChannelKeyPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  unsigned char *tmp, hash[32];
+  uint32 tmp_len;
+  char *cipher;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!packet->dst_id) {
-    SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
-    goto err;
-  }
-
-  /* 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;
+  /* Decode channel key payload */
+  payload = silc_channel_key_payload_parse(key_payload);
+  if (!payload) {
+    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    channel = NULL;
+    goto out;
   }
 
-  /* 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;
+  /* Get the channel entry */
+  if (!channel) {
 
-    /* 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);
+    /* 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;
     }
 
-    /* Seems that client really is directly connected to us */
-    silc_server_private_message_send_local(server, dst_sock, client, packet);
+    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"));
+       goto out;
+      }
+    }
+  }
+
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp) {
+    channel = NULL;
     goto out;
   }
 
-  /* 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;
+  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);
+  }
 
-    /* Send to primary route */
-    silc_server_private_message_send_internal(server, dst_sock, router,
-                                             packet);
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel = NULL;
     goto out;
   }
 
-  /* 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) {
+  if (channel->cipher)
+    silc_free(channel->cipher);
+  channel->cipher = strdup(cipher);
 
-    /* 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;
-    }
+  /* 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);
 
-    /* Send packet */
-    silc_server_private_message_send_internal(server, dst_sock, 
-                                             router, packet);
-    goto out;
+  /* 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);
   }
 
- err:
-  silc_server_send_error(server, sock, 
-                        "No such nickname: Private message not sent");
  out:
-  silc_buffer_free(buffer);
+  silc_free(id);
+  if (payload)
+    silc_channel_key_payload_free(payload);
+
+  return channel;
 }
 
-SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id)
+/* 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_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context)
 {
-  int i;
-  SilcIDCache *id_cache;
+  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-#define LCC(x) server->local_list->channel_cache[(x)]
-#define LCCC(x) server->local_list->channel_cache_count[(x)]
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
+                 sock->ip));
 
-  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;
-  }
-  
-  return NULL;
-#undef LCC
-#undef LCCC
+  /* Send the heartbeat */
+  silc_server_send_heartbeat(hb->server, sock);
 }
 
-/* Process received channel message. */
+/* Returns assembled of all servers in the given ID list. The packet's
+   form is dictated by the New ID payload. */
 
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcServerEntry remote,
+                                            SilcIDList id_list,
+                                            SilcBuffer *servers,
+                                            unsigned long creation_time)
 {
-  SilcChannelList *channel = NULL;
-  SilcChannelID *id = NULL;
-  SilcClientID *sender;
-  SilcBuffer buffer = packet->buffer;
-
-  SILC_LOG_DEBUG(("Processing channel message"));
-  
-  /* Check MAC */
-  if (!silc_server_packet_check_mac(server, sock, buffer))
-    goto out;
+  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;
+       }
 
-  /* 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;
-  }
+       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 
-  /* 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;
-  }
+       *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);
 
-  /* 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);
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
 
- 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 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_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 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;
+  SilcBuffer servers = NULL;
 
-  if (server->server_type == SILC_ROUTER)
-    goto out;
+  SILC_LOG_DEBUG(("Announcing servers"));
 
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
+  /* Get servers in local list */
+  silc_server_announce_get_servers(server, remote->user_data,
+                                  server->local_list, &servers,
+                                  creation_time);
 
-  /* 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"));
-  }
+  if (global)
+    /* Get servers in global list */
+    silc_server_announce_get_servers(server, remote->user_data,
+                                    server->global_list, &servers,
+                                    creation_time);
 
-  /* Get channel ID */
-  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
-  if (!id)
-    goto out;
+  if (servers) {
+    silc_buffer_push(servers, servers->data - servers->head);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->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_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           servers->data, servers->len, TRUE);
+
+    silc_buffer_free(servers);
   }
+}
 
-  /* 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);
+/* Returns assembled packet of all clients in the given ID list. The
+   packet's form is dictated by the New ID Payload. */
 
-  /* Distribute the key to all clients on the channel */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+static void silc_server_announce_get_clients(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *clients,
+                                            unsigned long creation_time)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client;
+  SilcBuffer idp;
+
+  /* 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)
-      silc_server_packet_send_dest(server, client->connection,
-                                  SILC_PACKET_CHANNEL_KEY, 0,
-                                  client->id, SILC_ID_CLIENT,
-                                  buffer->data, buffer->len, FALSE);
-  }
+       idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_key_free_payload(payload);
-  silc_buffer_free(buffer);
+       *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_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Sends error message. Error messages may or may not have any 
-   implications. */
+/* 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_send_error(SilcServer server,
-                           SilcSocketConnection sock,
-                           const char *fmt, ...)
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote)
 {
-  va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer clients = NULL;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  SILC_LOG_DEBUG(("Announcing clients"));
 
-  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
-                         buf, strlen(buf), FALSE);
-}
+  /* Get clients in local list */
+  silc_server_announce_get_clients(server, server->local_list,
+                                  &clients, creation_time);
 
-/* Sends notify message */
+  /* 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, creation_time);
 
-void silc_server_send_notify(SilcServer server,
-                            SilcSocketConnection sock,
-                            const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
+  if (clients) {
+    silc_buffer_push(clients, clients->data - clients->head);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           clients->data, clients->len, TRUE);
 
-  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         buf, strlen(buf), FALSE);
+    silc_buffer_free(clients);
+  }
 }
 
-/* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. */
-
-void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       const char *fmt, ...)
+static SilcBuffer 
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer p;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
+  va_start(ap, argc);
+  p = silc_notify_payload_encode(notify, argc, ap);
   va_end(ap);
-
-  silc_server_packet_send_to_channel(server, channel, buf, 
-                                    strlen(buf), FALSE);
+  return p;
 }
 
-/* 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)
+/* 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)
 {
-  SilcBuffer packet;
-  unsigned char *id_string;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
+  unsigned char mode[4];
 
-  id_string = silc_id_id2str(id, id_type);
-  if (!id_string)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  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);
+  /* 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_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);
+    silc_buffer_free(clidp);
+  }
+  silc_buffer_free(chidp);
 }
 
-/* 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)
+/* 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,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
-  SilcBuffer packet;
-  unsigned char *oid;
-  unsigned char *nid;
+  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;
 
-  oid = silc_id_id2str(old_id, old_id_type);
-  if (!oid)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  nid = silc_id_id2str(new_id, new_id_type);
-  if (!nid)
-    return;
+  /* 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);
+       }
 
-  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);
+       *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;
+       i++;
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
 
-  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);
+      *channel_users_modes_c += i;
+    }
+
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Creates new channel. */
+/* 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. */
 
-SilcChannelList *silc_server_new_channel(SilcServer server, 
-                                        SilcServerID *router_id,
-                                        char *cipher, char *channel_name)
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time,
+                                  SilcSocketConnection remote)
 {
-  int i, channel_len;
-  SilcChannelID *channel_id;
-  SilcChannelList *entry;
-  SilcCipher key;
-  unsigned char channel_key[32], *id_string;
-  SilcBuffer packet;
+  SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = 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_ids, creation_time);
+
+  /* Get channels and channel users in global list */
+  silc_server_announce_get_channels(server, server->global_list,
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids, creation_time);
+
+  if (channels) {
+    silc_buffer_push(channels, channels->data - channels->head);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
 
-  SILC_LOG_DEBUG(("Creating new channel"));
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
+                           channels->data, channels->len,
+                           FALSE);
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+    silc_buffer_free(channels);
+  }
 
-  /* Create channel key */
-  for (i = 0; i < 32; i++)
-    channel_key[i] = silc_rng_get_byte(server->rng);
+  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);
 
-  if (!cipher)
-    cipher = "twofish";
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           channel_users->data, channel_users->len,
+                           FALSE);
 
-  /* Allocate keys */
-  silc_cipher_alloc(cipher, &key);
-  key->cipher->set_key(key->context, channel_key, 16);
+    silc_buffer_free(channel_users);
+  }
 
-  /* 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));
+  if (channel_users_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      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);
+    silc_free(channel_ids);
+  }
+}
 
-  /* 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);
+/* 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). */
 
-    /* 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);
+SILC_TASK_CALLBACK(silc_server_failure_callback)
+{
+  SilcServerFailureContext f = (SilcServerFailureContext)context;
+
+  if (f->sock->protocol) {
+    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
   }
 
-#undef LCC
-#undef LCCC
-  return entry;
+  silc_free(f);
+}
+
+/* Assembles user list and users mode list from the `channel'. */
+
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     uint32 *user_count)
+{
+  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);
+
+  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_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;
 }
 
-/* Create new client. This processes incoming NEW_CLIENT packet and creates
-   Client ID for the client and adds it to lists and cache. */
+/* Saves users and their modes to the `channel'. */
 
-SilcClientList *silc_server_new_client(SilcServer server,
+void silc_server_save_users_on_channel(SilcServer server,
                                       SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      uint32 user_count)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientList *id_entry;
-  char *username = NULL, *realname = NULL, *id_string;
-  SilcBuffer reply;
+  int i;
+
+  for (i = 0; i < user_count; i++) {
+    uint16 idp_len;
+    uint32 mode;
+    SilcClientID *client_id;
+    SilcClientEntry client;
+
+    /* 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;
 
-  SILC_LOG_DEBUG(("Creating new client"));
+    /* Mode */
+    SILC_GET32_MSB(mode, mode_list->data);
+    silc_buffer_pull(mode_list, 4);
 
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
+    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+      silc_free(client_id);
+      continue;
+    }
+    
+    /* Check if we have this client cached already. */
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          server->server_type, NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, server->server_type,
+                                            NULL);
+    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;
+      }
 
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+      /* 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);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+       silc_free(client_id);
+       continue;
+      }
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
-                      SILC_STR_END);
+      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    }
 
-  /* 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;
+    silc_free(client_id);
+
+    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);
+    }
+  }
 }
 
-/* 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. */
+/* 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. */
 
-SilcServerList *silc_server_new_server(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 uint32 id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcServerList *id_entry;
-  unsigned char *server_name, *id_string;
+  SilcClientID *id;
+  SilcClientEntry client;
 
-  SILC_LOG_DEBUG(("Creating new server"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return NULL;
+  /* 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);
+  }
 
-#define LSC(x) server->local_list->server_cache[(x) - 32]
-#define LSCC(x) server->local_list->server_cache_count[(x) - 32]
+  /* 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);
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
+    /* 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;
+    }
 
-  /* 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);
+    /* Seems that client really is directly connected to us */
+    if (idata)
+      *idata = (SilcIDListData)client;
+    return client->connection;
+  }
 
-  silc_free(id_string);
+  /* 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;
+  }
+
+  /* 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;
+
+      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 LSC
-#undef LSCC
-  return id_entry;
+  silc_free(id);
+  return NULL;
 }
 
-/* Processes incoming New ID Payload. New ID Payload is used to distribute
-   information about newly registered clients, servers and created 
-   channels. */
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcIdType id_type;
-  unsigned char *id_string;
-  void *id;
+  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_LOG_DEBUG(("Processing new ID"));
+    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);
+  }
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
-    return;
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&id_type),
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_END);
+  return buffer;
+}
 
-  /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
-    goto out;
+/* Finds client entry by Client ID and if it is not found then resolves
+   it using WHOIS command. */
 
-  id = silc_id_str2id(id_string, id_type);
-  if (!id)
-    goto out;
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id)
+{
+  SilcClientEntry client;
 
-  /* 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 */
+  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;
+  }
 
-  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;
+  if (!client && server->standalone)
+    return NULL;
 
-  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;
+  if (!client || !client->nickname || !client->username) {
+    SilcBuffer buffer, idp;
+
+    client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+    client->resolve_cmd_ident = ++server->cmd_ident;
+
+    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);
+    return NULL;
+  }
 
-  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;
+  return client;
+}
 
-  default:
-    goto out;
-    break;
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *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->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);
+
+  /* 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);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+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;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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..1020a81a81f5be67ffdb01e851bc7a268405fcc5 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 timeuot grows when retry count
+   grows. */
+#define SILC_SERVER_RETRY_COUNT        4        /* Max retry count */
+#define SILC_SERVER_RETRY_MULTIPLIER   7 / 4    /* 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;
+  
+  /* 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_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,
+                                  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,
+                                 char *signoff);
 void silc_server_free_sock_user_data(SilcServer server, 
                                     SilcSocketConnection sock);
 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_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,
+                                      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);
 
 #endif
diff --git a/apps/silcd/server_backup.c b/apps/silcd/server_backup.c
new file mode 100644 (file)
index 0000000..423710f
--- /dev/null
@@ -0,0 +1,1235 @@
+/*
+
+  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_ne(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_ne(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->listen_port->local_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. */
+      SilcServerConfigSectionServerConnection *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);
+
+      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);
+       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..a40adec6a835bb3028befe18935fe658b5d5375d 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. */
+
+  /* Current command identifier, 0 not used */
+  uint16 cmd_ident;
+
+  /* SILC server scheduler */
+  SilcSchedule schedule;
 
   /* ID lists. */
   SilcIDList local_list;
@@ -60,7 +100,9 @@ typedef struct SilcServerObjectStruct {
   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 +113,65 @@ 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)
 
 /* 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..d048809
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+
+  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);
+       }
+       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_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);
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+
+       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);
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+
+       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))
+      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_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);
+           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);
+           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);
+           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);
+           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);
+  }
+}
+
+/* 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)
+      return TRUE;
+  }
+
+  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)
+      return TRUE;
+  }
+
+  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;
+
+  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
+    return TRUE;
+
+  return FALSE;
+}
diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h
new file mode 100644 (file)
index 0000000..00f740b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+
+  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);
+
+/* 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);
+
+#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..5aaec48955776bd5b3eaf0a85ef29d6d2e7fa163 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 "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>
-
-       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>
-
-*/
-SilcConfigServerSection silc_config_server_sections[] = {
+SilcServerConfigSection silc_server_config_sections[] = {
   { "[Cipher]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
   { "[PKCS]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 2 },
-  { "[HashFunction]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 1 },
+  { "[Hash]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
+  { "[hmac]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
+  { "[ServerKeys]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS, 2 },
   { "[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 },
+  { "[Identity]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY, 2 },
   { "[Logging]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
   { "[ConnectionClass]", 
@@ -176,13 +50,13 @@ SilcConfigServerSection silc_config_server_sections[] = {
   { "[ServerConnection]", 
     SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
   { "[RouterConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 6 },
+    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 7 },
   { "[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 },
+    SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 3 },
+  { "[motd]", 
+    SILC_CONFIG_SERVER_SECTION_TYPE_MOTD, 1 },
   
   { NULL, SILC_CONFIG_SERVER_SECTION_TYPE_NONE, 0 }
 };
@@ -191,11 +65,11 @@ SilcConfigServerSection silc_config_server_sections[] = {
    parses the file. The parsed data is returned to the newly allocated
    configuration object. */
 
-SilcConfigServer silc_config_server_alloc(char *filename)
+SilcServerConfig silc_server_config_alloc(char *filename)
 {
-  SilcConfigServer new;
+  SilcServerConfig new;
   SilcBuffer buffer;
-  SilcConfigServerParse config_parse;
+  SilcServerConfigParse config_parse;
 
   SILC_LOG_DEBUG(("Allocating new configuration object"));
 
@@ -207,15 +81,17 @@ SilcConfigServer silc_config_server_alloc(char *filename)
 
   new->filename = filename;
 
+  SILC_LOG_DEBUG(("Loading config data from `%s'", 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)
+  if ((silc_server_config_parse(new, buffer, &config_parse)) == FALSE)
     goto fail;
-  if ((silc_config_server_parse_lines(new, config_parse)) == FALSE)
+  if ((silc_server_config_parse_lines(new, config_parse)) == FALSE)
     goto fail;
 
   silc_free(buffer);
@@ -229,20 +105,22 @@ SilcConfigServer silc_config_server_alloc(char *filename)
 
 /* Free's a configuration object. */
 
-void silc_config_server_free(SilcConfigServer config)
+void silc_server_config_free(SilcServerConfig config)
 {
   if (config) {
     silc_free(config->filename);
+    silc_free(config->server_keys);
     silc_free(config->server_info);
     silc_free(config->admin_info);
     silc_free(config->listen_port);
+    silc_free(config->identity);
     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->motd);
     silc_free(config);
   }
 }
@@ -253,14 +131,13 @@ void silc_config_server_free(SilcConfigServer config)
    buffer sent as argument can be safely free'd after this function has
    succesfully returned. */
 
-int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer, 
-                            SilcConfigServerParse *return_config)
+int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer, 
+                            SilcServerConfigParse *return_config)
 {
-  int i, begin;
-  unsigned int linenum;
+  int i, begin, linenum;
   char line[1024], *cp;
-  SilcConfigServerSection *cptr = NULL;
-  SilcConfigServerParse parse = *return_config, first = NULL;
+  SilcServerConfigSection *cptr = NULL;
+  SilcServerConfigParse parse = *return_config, first = NULL;
 
   SILC_LOG_DEBUG(("Parsing configuration file"));
 
@@ -304,8 +181,8 @@ int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer,
        *strchr(cp, '\n') = '\0';
       
       /* Check for matching sections */
-      for (cptr = silc_config_server_sections; cptr->section; cptr++)
-       if (!strcmp(cp, cptr->section))
+      for (cptr = silc_server_config_sections; cptr->section; cptr++)
+       if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
          break;
 
       if (!cptr->section) {
@@ -370,13 +247,13 @@ int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer,
    parse_config argument is uninitialized automatically during this
    function. */
 
-int silc_config_server_parse_lines(SilcConfigServer config, 
-                                  SilcConfigServerParse parse_config)
+int silc_server_config_parse_lines(SilcServerConfig config, 
+                                  SilcServerConfigParse parse_config)
 {
   int ret, check = FALSE;
-  unsigned int checkmask;
+  uint32 checkmask;
   char *tmp;
-  SilcConfigServerParse pc = parse_config;
+  SilcServerConfigParse pc = parse_config;
   SilcBuffer line;
 
   SILC_LOG_DEBUG(("Parsing configuration lines"));
@@ -391,7 +268,7 @@ int silc_config_server_parse_lines(SilcConfigServer config,
 
     /* Get number of tokens in line */
     ret = silc_config_check_num_token(line);
-    if (ret != pc->section->maxfields) {
+    if (ret < pc->section->maxfields) {
       /* Bad line */
       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
              config->filename, pc->linenum, ret, 
@@ -421,28 +298,28 @@ int silc_config_server_parse_lines(SilcConfigServer 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;
@@ -463,18 +340,6 @@ int silc_config_server_parse_lines(SilcConfigServer 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;
       checkmask |= (1L << pc->section->type);
       break;
@@ -527,6 +392,94 @@ int silc_config_server_parse_lines(SilcConfigServer config,
       checkmask |= (1L << pc->section->type);
       break;
 
+    case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:
+
+      SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);
+
+      /* 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 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;
+      checkmask |= (1L << pc->section->type);
+      break;
+
+    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS:
+
+      if (!config->server_keys)
+       config->server_keys = silc_calloc(1, sizeof(*config->server_keys));
+
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: Public key name not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      
+      if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
+                                    SILC_PKCS_FILE_PEM))
+       if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
+                                      SILC_PKCS_FILE_BIN)) {
+         fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
+                 config->filename, pc->linenum, tmp);
+         break;
+       }
+      silc_free(tmp);
+
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: Private key name not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      
+      if (!silc_pkcs_load_private_key(tmp, &config->server_keys->private_key, 
+                                    SILC_PKCS_FILE_BIN))
+       if (!silc_pkcs_load_private_key(tmp, 
+                                       &config->server_keys->private_key, 
+                                       SILC_PKCS_FILE_PEM)) {
+         fprintf(stderr, "%s:%d: Could not load private key file `%s'\n",
+                 config->filename, pc->linenum, tmp);
+         break;
+       }
+      silc_free(tmp);
+
+      check = TRUE;
+      checkmask |= (1L << pc->section->type);
+      break;
+
     case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
 
       if (!config->server_info)
@@ -576,6 +529,11 @@ int silc_config_server_parse_lines(SilcConfigServer config,
       if (!config->admin_info)
        config->admin_info = silc_calloc(1, sizeof(*config->admin_info));
 
+      /* Get location */
+      ret = silc_config_get_token(line, &config->admin_info->location);
+      if (ret < 0)
+       break;
+
       /* Get server type */
       ret = silc_config_get_token(line, &config->admin_info->server_type);
       if (ret < 0)
@@ -599,13 +557,13 @@ int silc_config_server_parse_lines(SilcConfigServer config,
 
       SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->listen_port->host);
+      /* Get local IP */
+      ret = silc_config_get_token(line, &config->listen_port->local_ip);
       if (ret < 0)
        break;
 
-      /* Get remote IP */
-      ret = silc_config_get_token(line, &config->listen_port->remote_ip);
+      /* Get listener IP */
+      ret = silc_config_get_token(line, &config->listen_port->listener_ip);
       if (ret < 0)
        break;
 
@@ -625,6 +583,23 @@ int silc_config_server_parse_lines(SilcConfigServer config,
       checkmask |= (1L << pc->section->type);
       break;
 
+    case SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY:
+
+      if (!config->identity)
+        config->identity = silc_calloc(1, sizeof(*config->identity));
+
+      /* Get user */
+      ret = silc_config_get_token(line, &config->identity->user);
+      if (ret < 0)
+        break;
+      /* Get group */
+      ret = silc_config_get_token(line, &config->identity->group);
+      if (ret < 0)
+        break;
+
+      check = TRUE;
+      checkmask |= (1L << pc->section->type);
+
     case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:
 
       SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);
@@ -735,27 +710,45 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        }
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+         config->clients->auth_meth = SILC_AUTH_PASSWORD;
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+         config->clients->auth_meth = SILC_AUTH_PUBLIC_KEY;
 
        silc_free(tmp);
       }
 
       /* Get authentication data */
-      ret = silc_config_get_token(line, &config->clients->auth_data);
+      ret = silc_config_get_token(line, (char **)&config->clients->auth_data);
       if (ret < 0)
        break;
-      if (ret == 0)
-       /* Any host */
-       config->clients->host = strdup("*");
+
+      if (config->clients->auth_meth == SILC_AUTH_PASSWORD) {
+       config->clients->auth_data_len = strlen(config->clients->auth_data);
+      } else if (config->clients->auth_meth == SILC_AUTH_PUBLIC_KEY) {
+       /* Get the public key */
+       SilcPublicKey public_key;
+
+       if (!silc_pkcs_load_public_key(config->clients->auth_data,
+                                      &public_key, SILC_PKCS_FILE_PEM))
+         if (!silc_pkcs_load_public_key(config->clients->auth_data,
+                                        &public_key, SILC_PKCS_FILE_BIN)) {
+           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
+                   config->filename, pc->linenum, 
+                   (char *)config->clients->auth_data);
+           break;
+         }
+
+       silc_free(config->clients->auth_data);
+       config->clients->auth_data = (void *)public_key;
+       config->clients->auth_data_len = 0;
+      }
 
       /* Get port */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
        break;
-      if (ret == 0) {
+      if (ret) {
        config->clients->port = atoi(tmp);
        silc_free(tmp);
       }
@@ -798,19 +791,40 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        }
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+         config->servers->auth_meth = SILC_AUTH_PASSWORD;
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+         config->servers->auth_meth = SILC_AUTH_PUBLIC_KEY;
 
        silc_free(tmp);
       }
 
       /* Get authentication data */
-      ret = silc_config_get_token(line, &config->servers->auth_data);
+      ret = silc_config_get_token(line, (char **)&config->servers->auth_data);
       if (ret < 0)
        break;
 
+      if (config->servers->auth_meth == SILC_AUTH_PASSWORD) {
+       config->servers->auth_data_len = strlen(config->servers->auth_data);
+      } else if (config->servers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
+       /* Get the public key */
+       SilcPublicKey public_key;
+
+       if (!silc_pkcs_load_public_key(config->servers->auth_data,
+                                      &public_key, SILC_PKCS_FILE_PEM))
+         if (!silc_pkcs_load_public_key(config->servers->auth_data,
+                                        &public_key, SILC_PKCS_FILE_BIN)) {
+           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
+                   config->filename, pc->linenum, 
+                   (char *)config->servers->auth_data);
+           break;
+         }
+
+       silc_free(config->servers->auth_data);
+       config->servers->auth_data = (void *)public_key;
+       config->servers->auth_data_len = 0;
+      }
+
       /* Get port */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
@@ -834,6 +848,15 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        silc_free(tmp);
       }
 
+      /* Check whether this connection is backup router connection */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret != -1) {
+       config->servers->backup_router = atoi(tmp);
+       if (config->servers->backup_router != 0)
+         config->servers->backup_router = TRUE;
+       silc_free(tmp);
+      }
+
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
@@ -846,9 +869,6 @@ int silc_config_server_parse_lines(SilcConfigServer config,
       ret = silc_config_get_token(line, &config->routers->host);
       if (ret < 0)
        break;
-      //      if (ret == 0)
-      ///* Any host */
-      //       config->routers->host = strdup("*");
 
       /* Get authentication method */
       ret = silc_config_get_token(line, &tmp);
@@ -863,19 +883,40 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        }
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+         config->routers->auth_meth = SILC_AUTH_PASSWORD;
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+         config->routers->auth_meth = SILC_AUTH_PUBLIC_KEY;
 
        silc_free(tmp);
       }
 
       /* Get authentication data */
-      ret = silc_config_get_token(line, &config->routers->auth_data);
+      ret = silc_config_get_token(line, (char **)&config->routers->auth_data);
       if (ret < 0)
        break;
 
+      if (config->routers->auth_meth == SILC_AUTH_PASSWORD) {
+       config->routers->auth_data_len = strlen(config->routers->auth_data);
+      } else if (config->routers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
+       /* Get the public key */
+       SilcPublicKey public_key;
+
+       if (!silc_pkcs_load_public_key(config->routers->auth_data,
+                                      &public_key, SILC_PKCS_FILE_PEM))
+         if (!silc_pkcs_load_public_key(config->routers->auth_data,
+                                        &public_key, SILC_PKCS_FILE_BIN)) {
+           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
+                   config->filename, pc->linenum, 
+                   (char *)config->routers->auth_data);
+           break;
+         }
+
+       silc_free(config->routers->auth_data);
+       config->routers->auth_data = (void *)public_key;
+       config->routers->auth_data_len = 0;
+      }
+
       /* Get port */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
@@ -899,6 +940,40 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        silc_free(tmp);
       }
 
+      /* Get whether we are initiator or not */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret) {
+       config->routers->initiator = atoi(tmp);
+       if (config->routers->initiator != 0)
+         config->routers->initiator = TRUE;
+       silc_free(tmp);
+      }
+
+      /* Get backup replace IP */
+      ret = silc_config_get_token(line, &config->routers->backup_replace_ip);
+      if (ret != -1)
+       config->routers->backup_router = TRUE;
+
+      if (config->routers->backup_router) {
+       /* Get backup replace port */
+       ret = silc_config_get_token(line, &tmp);
+       if (ret != -1) {
+         config->routers->backup_replace_port = atoi(tmp);
+         silc_free(tmp);
+       }
+       
+       /* Check whether the backup connection is local */
+       ret = silc_config_get_token(line, &tmp);
+       if (ret != -1) {
+         config->routers->backup_local = atoi(tmp);
+         if (config->routers->backup_local != 0)
+           config->routers->backup_local = TRUE;
+         silc_free(tmp);
+       }
+      }
+
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
@@ -915,6 +990,22 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        /* Any host */
        config->admins->host = strdup("*");
 
+      /* Get username */
+      ret = silc_config_get_token(line, &config->admins->username);
+      if (ret < 0)
+       break;
+      if (ret == 0)
+       /* Any username */
+       config->admins->username = strdup("*");
+
+      /* Get nickname */
+      ret = silc_config_get_token(line, &config->admins->nickname);
+      if (ret < 0)
+       break;
+      if (ret == 0)
+       /* Any nickname */
+       config->admins->nickname = strdup("*");
+
       /* Get authentication method */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
@@ -928,45 +1019,92 @@ int silc_config_server_parse_lines(SilcConfigServer config,
        }
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+         config->admins->auth_meth = SILC_AUTH_PASSWORD;
 
        if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+         config->admins->auth_meth = SILC_AUTH_PUBLIC_KEY;
 
        silc_free(tmp);
       }
 
       /* Get authentication data */
-      ret = silc_config_get_token(line, &config->admins->auth_data);
+      ret = silc_config_get_token(line, (char **)&config->admins->auth_data);
       if (ret < 0)
        break;
 
-      /* Get nickname */
-      ret = silc_config_get_token(line, &config->admins->nickname);
+      if (config->admins->auth_meth == SILC_AUTH_PASSWORD) {
+       config->admins->auth_data_len = strlen(config->admins->auth_data);
+      } else if (config->admins->auth_meth == SILC_AUTH_PUBLIC_KEY) {
+       /* Get the public key */
+       SilcPublicKey public_key;
+
+       if (!silc_pkcs_load_public_key(config->admins->auth_data,
+                                      &public_key, SILC_PKCS_FILE_PEM))
+         if (!silc_pkcs_load_public_key(config->admins->auth_data,
+                                        &public_key, SILC_PKCS_FILE_BIN)) {
+           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
+                   config->filename, pc->linenum, 
+                   (char *)config->admins->auth_data);
+           break;
+         }
+
+       silc_free(config->admins->auth_data);
+       config->admins->auth_data = (void *)public_key;
+       config->admins->auth_data_len = 0;
+      }
+
+      check = TRUE;
+      checkmask |= (1L << pc->section->type);
+      break;
+
+    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
+
+      SILC_SERVER_CONFIG_LIST_ALLOC(config->denied);
+
+      /* Get host */
+      ret = silc_config_get_token(line, &config->denied->host);
       if (ret < 0)
        break;
+      if (ret == 0) {
+       /* Any host */
+       config->denied->host = strdup("*");
+       fprintf(stderr, "warning: %s:%d: Denying all connections",
+               config->filename, pc->linenum);
+      }
 
-      /* Get class number */
+      /* Get port */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
        break;
-      if (ret) {
-       config->admins->class = atoi(tmp);
+      if (ret == 0) {
+       /* Any port */
+       config->denied->port = 0;
+      } else {
+       config->denied->port = atoi(tmp);
        silc_free(tmp);
       }
 
+      /* Get comment */
+      ret = silc_config_get_token(line, &config->denied->comment);
+      if (ret < 0)
+       break;
+
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
-      /* Not implemented yet */
-      check = TRUE;
-      break;
+    case SILC_CONFIG_SERVER_SECTION_TYPE_MOTD:
+
+      if (!config->motd)
+       config->motd = silc_calloc(1, sizeof(*config->motd));
+
+      /* Get motd file */
+      ret = silc_config_get_token(line, &config->motd->motd_file);
+      if (ret < 0)
+       break;
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT:
-      /* Not implemented yet */
       check = TRUE;
+      checkmask |= (1L << pc->section->type);
       break;
 
     case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
@@ -983,9 +1121,6 @@ int silc_config_server_parse_lines(SilcConfigServer config,
     }
 
     pc = pc->next;
-    /* XXXX */
-    //    silc_free(pc->prev);
-    //    pc->prev = NULL;
   }
 
   if (check == FALSE)
@@ -993,7 +1128,7 @@ int silc_config_server_parse_lines(SilcConfigServer config,
 
   /* 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);
+  ret = silc_server_config_check_sections(checkmask);
   if (ret == FALSE) {
     /* XXX */
 
@@ -1007,6 +1142,8 @@ int silc_config_server_parse_lines(SilcConfigServer 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->listen_port && config->listen_port->prev)
     config->listen_port = config->listen_port->prev;
   while (config->logging && config->logging->prev)
@@ -1017,6 +1154,8 @@ int silc_config_server_parse_lines(SilcConfigServer config,
     config->clients = config->clients->prev;
   while (config->servers && config->servers->prev)
     config->servers = config->servers->prev;
+  while (config->admins && config->admins->prev)
+    config->admins = config->admins->prev;
   while (config->routers && config->routers->prev)
     config->routers = config->routers->prev;
   
@@ -1028,7 +1167,7 @@ int silc_config_server_parse_lines(SilcConfigServer 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)
+int silc_server_config_check_sections(uint32 checkmask)
 {
   if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO))) {
     
@@ -1042,7 +1181,8 @@ int silc_config_server_check_sections(unsigned int checkmask)
     
     return FALSE;
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
+  if (!(checkmask & 
+       (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
     
     return FALSE;
   }
@@ -1062,11 +1202,11 @@ int silc_config_server_check_sections(unsigned int checkmask)
 
 /* Sets log files where log messages is saved by the server. */
 
-void silc_config_server_setlogfiles(SilcConfigServer config)
+void silc_server_config_setlogfiles(SilcServerConfig config)
 {
-  SilcConfigServerSectionLogging *log;
+  SilcServerConfigSectionLogging *log;
   char *info, *warning, *error, *fatal;
-  unsigned int info_size, warning_size, error_size, fatal_size;
+  uint32 info_size, warning_size, error_size, fatal_size;
 
   SILC_LOG_DEBUG(("Setting configured log file names"));
 
@@ -1109,33 +1249,39 @@ void silc_config_server_setlogfiles(SilcConfigServer config)
 /* 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(SilcServerConfig config)
 {
-  SilcConfigServerSectionAlg *alg;
+  SilcServerConfigSectionAlg *alg;
   SilcServer server = (SilcServer)config->server;
 
   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;
-
-      if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
-       SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
+      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_is_supported(alg->alg_name)) {
+       SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
        silc_server_stop(server);
        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;
@@ -1146,34 +1292,36 @@ void silc_config_server_register_ciphers(SilcConfigServer 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_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 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);
@@ -1187,59 +1335,73 @@ void silc_config_server_register_ciphers(SilcConfigServer 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_config_server_register_pkcs(SilcConfigServer config)
+bool silc_server_config_register_pkcs(SilcServerConfig config)
 {
-  SilcConfigServerSectionAlg *alg = config->pkcs;
+  SilcServerConfigSectionAlg *alg = config->pkcs;
   SilcServer server = (SilcServer)config->server;
-  SilcPKCS tmp = NULL;
 
   SILC_LOG_DEBUG(("Registering configured PKCS"));
 
-  while(alg) {
+  if (!config->pkcs)
+    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_server_stop(server);
       exit(1);
     }
-    silc_free(tmp);
 
     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(SilcServerConfig config)
 {
-  SilcConfigServerSectionAlg *alg;
+  SilcServerConfigSectionAlg *alg;
   SilcServer server = (SilcServer)config->server;
 
   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 funtion `%s'", alg->alg_name));
        silc_server_stop(server);
        exit(1);
       }
-      silc_free(tmp);
 
 #ifdef SILC_SIM
     } else {
@@ -1275,35 +1437,70 @@ void silc_config_server_register_hashfuncs(SilcConfigServer config)
        SILC_LOG_DEBUG(("context_len=%p", hash.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 */
+      /* Register the hash function */
       silc_hash_register(&hash);
 #endif
     }
 
     alg = alg->next;
   }
+
+  return TRUE;
+}
+
+/* Registers configure HMACs. These can then be allocated by the server
+   when needed. */
+
+bool silc_server_config_register_hmacs(SilcServerConfig config)
+{
+  SilcServerConfigSectionAlg *alg;
+  SilcServer server = (SilcServer)config->server;
+
+  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'", alg->sim_name));
+      silc_server_stop(server);
+      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;
 }
 
 /* Returns client authentication information from server configuration
-   by host (name or ip). */
+   by host (name or ip). If `port' is non-null then both name or IP and 
+   the port must match. */
 
-SilcConfigServerSectionClientConnection *
-silc_config_server_find_client_conn(SilcConfigServer config, 
+SilcServerConfigSectionClientConnection *
+silc_server_config_find_client_conn(SilcServerConfig config, 
                                    char *host, int port)
 {
   int i;
-  SilcConfigServerSectionClientConnection *client = NULL;
+  SilcServerConfigSectionClientConnection *client = NULL;
+  bool match = FALSE;
 
   if (!host)
     return NULL;
@@ -1315,7 +1512,14 @@ silc_config_server_find_client_conn(SilcConfigServer config,
 
   for (i = 0; client; i++) {
     if (silc_string_compare(client->host, host))
+      match = TRUE;
+
+    if (port && client->port && client->port != port)
+      match = FALSE;
+
+    if (match)
       break;
+
     client = client->next;
   }
 
@@ -1326,14 +1530,16 @@ silc_config_server_find_client_conn(SilcConfigServer config,
 }
 
 /* Returns server connection info from server configuartion by host 
-   (name or ip). */
+   (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, 
+SilcServerConfigSectionServerConnection *
+silc_server_config_find_server_conn(SilcServerConfig config, 
                                    char *host, int port)
 {
   int i;
-  SilcConfigServerSectionServerConnection *serv = NULL;
+  SilcServerConfigSectionServerConnection *serv = NULL;
+  bool match = FALSE;
 
   if (!host)
     return NULL;
@@ -1344,7 +1550,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;
   }
 
@@ -1357,12 +1570,13 @@ silc_config_server_find_server_conn(SilcConfigServer config,
 /* Returns router connection info from server configuartion by
    host (name or ip). */
 
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_router_conn(SilcConfigServer config, 
+SilcServerConfigSectionServerConnection *
+silc_server_config_find_router_conn(SilcServerConfig config, 
                                    char *host, int port)
 {
   int i;
-  SilcConfigServerSectionServerConnection *serv = NULL;
+  SilcServerConfigSectionServerConnection *serv = NULL;
+  bool match = FALSE;
 
   if (!host)
     return NULL;
@@ -1373,7 +1587,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 +1604,115 @@ silc_config_server_find_router_conn(SilcConfigServer config,
   return serv;
 }
 
-/* Prints out example configuration file with default built in
-   configuration values. */
+/* Returns TRUE if configuartion 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;
+  int i;
+  SilcServerConfigSectionServerConnection *serv = NULL;
+  bool found = FALSE;
 
-  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
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE) {
+      found = TRUE;
+      break;
+    }
+
+    serv = serv->next;
+  }
+
+  return found;
+}
+
+/* Returns our primary connection configuration or NULL if we do not
+   have primary router configured. */
+
+SilcServerConfigSectionServerConnection *
+silc_server_config_get_primary_router(SilcServerConfig config)
+{
+  int i;
+  SilcServerConfigSectionServerConnection *serv = NULL;
+
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE)
+      return serv;
+    serv = serv->next;
+  }
+
+  return NULL;
+}
 
-#<HashFunction>
-#+md5
-#+sha1
+/* Returns Admin connection configuration by host, username and/or 
+   nickname. */
 
-<ServerInfo>
-+lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1333
+SilcServerConfigSectionAdminConnection *
+silc_server_config_find_admin(SilcServerConfig config,
+                             char *host, char *username, char *nickname)
+{
+  SilcServerConfigSectionAdminConnection *admin = NULL;
+  int i;
 
-<AdminInfo>
-+Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
+  if (!config->admins)
+    return NULL;
 
-<ListenPort>
-+10.2.1.6:10.2.1.6:1333
+  if (!host)
+    host = "*";
+  if (!username)
+    username = "*";
+  if (!nickname)
+    nickname = "*";
+
+  admin = config->admins;
+  for (i = 0; admin; i++) {
+    if (silc_string_compare(admin->host, host) &&
+       silc_string_compare(admin->username, username) &&
+       silc_string_compare(admin->nickname, nickname))
+      break;
 
-<Logging>
-+infologfile:silcd.log:10000
-#+warninglogfile:/var/log/silcd_warning.log:10000
-#+errorlogfile:ERROR.log:10000
-#+fatallogfile:/var/log/silcd_error.log:
+    admin = admin->next;
+  }
 
-<ConnectionClass>
-               +1:100:100:100
-                       +2:200:300:400
+  if (!admin)
+    return NULL;
 
-<ClientAuth>
-+10.2.1.199:priikone:333:1
+  return admin;
+}
 
-<AdminAuth>
-+10.2.1.199:priikone:priikone:1
+/* Returns the Denied connection configuration by host and port. */
 
-<ServerConnection>
+SilcServerConfigSectionDenyConnection *
+silc_server_config_denied_conn(SilcServerConfig config, char *host,
+                              int port)
+{
+  int i;
+  SilcServerConfigSectionDenyConnection *deny = NULL;
+  bool match = FALSE;
 
-<RouterConnection>
+  if (!host)
+    return NULL;
+
+  if (!config->denied)
+    return NULL;
+
+  deny = config->denied;
+  for (i = 0; deny; i++) {
+    if (silc_string_compare(deny->host, host))
+      match = TRUE;
+
+    if (port && deny->port && deny->port != port)
+      match = FALSE;
 
-<DenyConnection>
-<RedirectClient>
-  */
+    if (match)
+      break;
+
+    deny = deny->next;
+  }
+
+  if (!deny)
+    return NULL;
 
-  fprintf(stdout, "%s\n", buf);
+  return deny;
 }
index 56ed10eb129e107081e2aad1f7fa0e4863798832..1df1e95a4115dac69818d6dc3a5513476e8af58b 100644 (file)
 #define SERVERCONFIG_H
 
 /* Holds information of configured algorithms */
-typedef struct SilcConfigServerSectionAlgStruct {
+typedef struct SilcServerConfigSectionAlgStruct {
   char *alg_name;
   char *sim_name;
-  unsigned int block_len;
-  unsigned int key_len;
-  struct SilcConfigServerSectionAlgStruct *next;
-  struct SilcConfigServerSectionAlgStruct *prev;
+  uint32 block_len;
+  uint32 key_len;
+  struct SilcServerConfigSectionAlgStruct *next;
+  struct SilcServerConfigSectionAlgStruct *prev;
 #define SILC_CONFIG_SERVER_MODNAME "builtin"
-} SilcConfigServerSectionAlg;
+} SilcServerConfigSectionAlg;
+
+/* Holds server keys from config file */
+typedef struct {
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+} SilcServerConfigSectionServerKeys;
 
 /* Holds server information from config file */
 typedef struct {
   char *server_name;
   char *server_ip;
   char *location;
-  unsigned short port;
-} SilcConfigServerSectionServerInfo;
+  uint16 port;
+} SilcServerConfigSectionServerInfo;
 
 /* Holds server's administrative information from config file */
 typedef struct {
+  char *location;
   char *server_type;
   char *admin_name;
   char *admin_email;
-} SilcConfigServerSectionAdminInfo;
+} SilcServerConfigSectionAdminInfo;
 
 /* 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;
+typedef struct SilcServerConfigSectionListenPortStruct {
+  char *local_ip;
+  char *listener_ip;
+  uint16 port;
+  struct SilcServerConfigSectionListenPortStruct *next;
+  struct SilcServerConfigSectionListenPortStruct *prev;
+} SilcServerConfigSectionListenPort;
+
+/* Holds server's execution identity, or the user and group which
+   to change from root when server starts */
+typedef struct {
+  char *user;
+  char *group;
+} SilcServerConfigSectionIdentity;
 
 /* Holds all the configured log files. */
-typedef struct SilcConfigServerSectionLoggingStruct {
+typedef struct SilcServerConfigSectionLoggingStruct {
   char *logtype;
   char *filename;
-  unsigned int maxsize;
-  struct SilcConfigServerSectionLoggingStruct *next;
-  struct SilcConfigServerSectionLoggingStruct *prev;
+  uint32 maxsize;
+  struct SilcServerConfigSectionLoggingStruct *next;
+  struct SilcServerConfigSectionLoggingStruct *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;
+#define SILC_CONFIG_SERVER_LF_FATAL "fatallogfile"
+} 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;
+typedef struct SilcServerConfigSectionConnectionClassStruct {
+  uint32 class;
+  uint32 ping_freq;
+  uint32 connect_freq;
+  uint32 max_links;
+  struct SilcServerConfigSectionConnectionClassStruct *next;
+  struct SilcServerConfigSectionConnectionClassStruct *prev;
+} SilcServerConfigSectionConnectionClass;
 
 #define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
 #define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
 
 /* Holds all client authentication data from config file */
-typedef struct SilcConfigServerSectionClientConnectionStruct {
+typedef struct SilcServerConfigSectionClientConnectionStruct {
   char *host;
-  int auth_meth;
-  char *auth_data;
-  unsigned short port;
-  unsigned int class;
-  struct SilcConfigServerSectionClientConnectionStruct *next;
-  struct SilcConfigServerSectionClientConnectionStruct *prev;
-} SilcConfigServerSectionClientConnection;
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  uint16 port;
+  uint32 class;
+  struct SilcServerConfigSectionClientConnectionStruct *next;
+  struct SilcServerConfigSectionClientConnectionStruct *prev;
+} SilcServerConfigSectionClientConnection;
 
 /* Hols all server's administrators authentication data from config file */
-typedef struct SilcConfigServerSectionAdminConnectionStruct {
+typedef struct SilcServerConfigSectionAdminConnectionStruct {
   char *host;
-  int auth_meth;
-  char *auth_data;
+  char *username;
   char *nickname;
-  unsigned int class;
-  struct SilcConfigServerSectionAdminConnectionStruct *next;
-  struct SilcConfigServerSectionAdminConnectionStruct *prev;
-} SilcConfigServerSectionAdminConnection;
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  struct SilcServerConfigSectionAdminConnectionStruct *next;
+  struct SilcServerConfigSectionAdminConnectionStruct *prev;
+} SilcServerConfigSectionAdminConnection;
 
 /* Holds all configured server/router connections from config file */
-typedef struct SilcConfigServerSectionServerConnectionStruct {
+typedef struct SilcServerConfigSectionServerConnectionStruct {
   char *host;
-  int auth_meth;
-  char *auth_data;
-  unsigned short port;
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  uint16 port;
   char *version;
-  unsigned int class;
-  struct SilcConfigServerSectionServerConnectionStruct *next;
-  struct SilcConfigServerSectionServerConnectionStruct *prev;
-} SilcConfigServerSectionServerConnection;
+  uint32 class;
+  bool initiator;
+  bool backup_router;
+  char *backup_replace_ip;
+  uint16 backup_replace_port;
+  bool backup_local;
+  struct SilcServerConfigSectionServerConnectionStruct *next;
+  struct SilcServerConfigSectionServerConnectionStruct *prev;
+} SilcServerConfigSectionServerConnection;
 
 /* Holds all configured denied connections from config file */
-typedef struct {
+typedef struct SilcServerConfigSectionDenyConnectionStruct {
   char *host;
-  char *time;
   char *comment;
-  unsigned short port;
-} SilcConfigServerSectionDenyConnection;
+  uint16 port;
+  struct SilcServerConfigSectionDenyConnectionStruct *next;
+  struct SilcServerConfigSectionDenyConnectionStruct *prev;
+} SilcServerConfigSectionDenyConnection;
 
-/* Holds all client redirections from config file */
+/* Holds motd file */
 typedef struct {
-  char *host;
-  unsigned short port;
-} SilcConfigServerSectionRedirectClient;
+  char *motd_file;
+} SilcServerConfigSectionMotd;
 
 /* 
    SILC Server Config object. 
@@ -147,23 +169,26 @@ typedef struct {
   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;
+  SilcServerConfigSectionAlg *cipher;
+  SilcServerConfigSectionAlg *pkcs;
+  SilcServerConfigSectionAlg *hash_func;
+  SilcServerConfigSectionAlg *hmac;
+  SilcServerConfigSectionServerKeys *server_keys;
+  SilcServerConfigSectionServerInfo *server_info;
+  SilcServerConfigSectionAdminInfo *admin_info;
+  SilcServerConfigSectionListenPort *listen_port;
+  SilcServerConfigSectionIdentity *identity;
+  SilcServerConfigSectionLogging *logging;
+  SilcServerConfigSectionConnectionClass *conn_class;
+  SilcServerConfigSectionClientConnection *clients;
+  SilcServerConfigSectionServerConnection *servers;
+  SilcServerConfigSectionServerConnection *routers;
+  SilcServerConfigSectionAdminConnection *admins;
+  SilcServerConfigSectionDenyConnection *denied;
+  SilcServerConfigSectionMotd *motd;
+} SilcServerConfigObject;
+
+typedef SilcServerConfigObject *SilcServerConfig;
 
 /* Configuration section type enumerations. */
 typedef enum {
@@ -171,9 +196,12 @@ typedef enum {
   SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
   SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
   SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
+  SILC_CONFIG_SERVER_SECTION_TYPE_HMAC,
+  SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS,
   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_IDENTITY,
   SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING,
   SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS,
   SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION,
@@ -181,28 +209,28 @@ typedef enum {
   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_CONFIG_SERVER_SECTION_TYPE_MOTD,
+} SilcServerConfigSectionType;
 
 /* SILC Configuration Section structure. */
 typedef struct {
   const char *section;
-  SilcConfigServerSectionType type;
-  unsigned int maxfields;
-} SilcConfigServerSection;
+  SilcServerConfigSectionType type;
+  int maxfields;
+} SilcServerConfigSection;
 
 /* LIst of all possible config sections in SILC server. */
-extern SilcConfigServerSection silc_config_server_sections[];
+extern SilcServerConfigSection silc_server_config_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 {
+typedef struct SilcServerConfigParseStruct {
   SilcBuffer line;
-  unsigned int linenum;
-  SilcConfigServerSection *section;
-  struct SilcConfigServerParseStruct *next;
-  struct SilcConfigServerParseStruct *prev;
-} *SilcConfigServerParse;
+  int linenum;
+  SilcServerConfigSection *section;
+  struct SilcServerConfigParseStruct *next;
+  struct SilcServerConfigParseStruct *prev;
+} *SilcServerConfigParse;
 
 /* Macros */
 
@@ -225,26 +253,35 @@ do {                                                      \
 } while(0)
 
 /* 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, 
+SilcServerConfig silc_server_config_alloc(char *filename);
+void silc_server_config_free(SilcServerConfig config);
+int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer,
+                            SilcServerConfigParse *return_config);
+int silc_server_config_parse_lines(SilcServerConfig config, 
+                                  SilcServerConfigParse parse_config);
+int silc_server_config_check_sections(uint32 checkmask);
+void silc_server_config_setlogfiles(SilcServerConfig config);
+bool silc_server_config_register_ciphers(SilcServerConfig config);
+bool silc_server_config_register_pkcs(SilcServerConfig config);
+bool silc_server_config_register_hashfuncs(SilcServerConfig config);
+bool silc_server_config_register_hmacs(SilcServerConfig config);
+SilcServerConfigSectionClientConnection *
+silc_server_config_find_client_conn(SilcServerConfig config, 
                                    char *host, int port);
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_server_conn(SilcConfigServer config, 
+SilcServerConfigSectionServerConnection *
+silc_server_config_find_server_conn(SilcServerConfig config, 
                                    char *host, int port);
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_router_conn(SilcConfigServer config, 
+SilcServerConfigSectionServerConnection *
+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);
+SilcServerConfigSectionServerConnection *
+silc_server_config_get_primary_router(SilcServerConfig config);
+SilcServerConfigSectionAdminConnection *
+silc_server_config_find_admin(SilcServerConfig config,
+                             char *host, char *username, char *nickname);
+SilcServerConfigSectionDenyConnection *
+silc_server_config_denied_conn(SilcServerConfig config, char *host,
+                              int port);
 
 #endif
index 767faace3c1b002e3d17e2257919847aba6619bc..ff0c551bb0c04f626fa1ab80a8525288f94ad33d 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. */
@@ -39,10 +33,6 @@ void silc_id_create_server_id(int sock, SilcRng rng, SilcServerID **new_id)
   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);
@@ -55,51 +45,106 @@ void silc_id_create_server_id(int sock, SilcRng rng, SilcServerID **new_id)
   }
 
   /* Create the ID */
-  (*new_id)->ip = server.sin_addr;
+  /* XXX Does not support IPv6 */
+  SILC_PUT32_MSB(server.sin_addr.s_addr, (*new_id)->ip.data);
+  (*new_id)->ip.data_len = 4;
   (*new_id)->port = server.sin_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..4a81a4a44e83273c0f39cc91a3e8f78b42edf45e 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
 
 /* 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,
+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..be913daa56c09229fb3b373a0fea73fc37a723f7 100644 (file)
 
 /* SILC Server includes */
 #include "idlist.h"
-#include "route.h"
-#include "serverid.h"
 #include "serverconfig.h"
 #include "server.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..abc53ac7064f3480409b533c8f3347040573863a 100644 (file)
@@ -2,9 +2,9 @@
 
   silcd.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
  * 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"
 
+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[] = 
 {
   { "config-file", 1, NULL, 'f' },
-  { "generate-config-file", 0, NULL, 'c' },
+  { "debug", 0, NULL, 'd' },
   { "help", 0, NULL, 'h' },
   { "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                   Enable debugging (no daemon)\n\
+  -h  --help                    Display this message\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);
 }
 
@@ -66,10 +101,14 @@ int main(int argc, char **argv)
   int opt, option_index;
   char *config_file = NULL;
   SilcServer silcd;
+  struct sigaction sa;
+  char pid[10];
+
+  silc_debug = FALSE;
 
   /* Parse command line arguments */
   if (argc > 1) {
-    while ((opt = getopt_long(argc, argv, "cf:hV",
+    while ((opt = getopt_long(argc, argv, "cf:dhVC:",
                              long_opts, &option_index)) != EOF) {
       switch(opt) 
        {
@@ -78,19 +117,40 @@ int main(int argc, char **argv)
          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':
+         silc_debug = TRUE;
          break;
        case 'f':
          config_file = strdup(optarg);
          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 +158,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,7 +179,7 @@ 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;
 
@@ -116,6 +187,23 @@ int main(int argc, char **argv)
   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);
+
+  if (silc_debug == FALSE)
+    /* Before running the server, fork to background and set
+       both user and group no non-root */    
+    silc_server_daemonise(silcd);
+
+  /* Set /var/run/silcd.pid */
+  unlink(SILC_SERVER_PID_FILE);
+  memset(pid, 0, sizeof(pid));
+  snprintf(pid, sizeof(pid) - 1, "%d\n", getpid());
+  silc_file_writefile(SILC_SERVER_PID_FILE, pid, strlen(pid));
   
   /* Run the server. When this returns the server has been stopped
      and we will exit. */
@@ -130,3 +218,107 @@ int main(int argc, char **argv)
  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..a9d7b6f30431ef30d37b5b8fbb6562a835cdba33 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 */
+#ifndef SILC_SERVER_PID_FILE
+#define SILC_SERVER_PID_FILE "/var/run/silcd.pid"
+#endif
 
-#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]
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..ef9c084
--- /dev/null
@@ -0,0 +1,771 @@
+#
+#  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
+
+# 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 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)
+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") ])
+
+AC_ARG_WITH(silcd-pid-file,
+[  --with-silcd-pid-file[=PATH]
+                          Use PATH as default pid file in SILC
+                          server [/var/run/silcd.pid]],
+[ AC_DEFINE_UNQUOTED(SILC_SERVER_PID_FILE, "$withval") ])
+
+#
+# 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)
+
+#
+# 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 -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(lib/dotconf)
+AC_CONFIG_SUBDIRS(lib/trq)
+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/silcsim/modules/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..e55e5e0
--- /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
+
+# Irssi SILC Client distribution
+client_SUBDIRS=lib irssi doc includes
+client_SUBDIRS_lib=contrib silccore silccrypt silcsim silcmath silcske silcutil trq 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 trq dotconf 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 trq silcclient
+win32dll_SUBDIRS_doc=$(COMMONDIRS)
+win32dll_DISTLABEL=SILC_DIST_WIN32DLL
+win32dll_EXTRA_DIST=README.CVS README.WIN32
+
+DISTRIBUTIONS=toolkit client server win32dll
index 0393f347ca1e73a3555fa04eca0a0873afa55760..3f34557f8d1af6cd0659ae085aef6be2f3603f6e 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.
+Both are based on the TRQ library.  For definitions of List API see
+lib/trq/silclist.h and for Dynamic List API see lib/trq/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..44d9056616ea846ee217ec0b40335a980a4c70da 100644 (file)
--- a/doc/FAQ
+++ b/doc/FAQ
@@ -4,25 +4,90 @@ Frequently Asked 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
-   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.
+   SILC superficially resembles IRC 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.
 
-   Biggest differences are that SILC is secure what IRC is not in any
-   way.  The network model is also entirely different compared to IRC.
+
+Q: Why SILC in the first place?
+A: Simply for fun, nothing more.  An actually for need back then when
+   it was started.  SILC has been very interesting and educational
+   project.
+
+
+Q: When SILC will be completed?
+A: SILC still has a lot things to do.  The time of completion is much
+   related to how many interested people is willing to join the effort.
+   It will be ready when it is ready.  The reason for release of the
+   current development version is just to get it out and people aware
+   that something like this exist.
+
+
+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?  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 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
+   with SILC server.  SILC client cannot currently 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: Why client/server protocol is based on IRC? Would it be more
+   interesting to implement something extensible and more powerful?
+A: They are not, not the least.  Have you read the protocol
+   specification?  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, I've taken good
+   things from IRC and leaved all the bad things behind and not even tried
+   to burden myself with the IRC caveats that will burden IRC and future
+   IRC projects til 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: 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.
+
+   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 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: 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
@@ -51,7 +116,7 @@ A: A good question which I don't have a answer.  SILC has been tried to
         the public keys used in the SILC are not verified to be trusted.
 
       o IP spoofing is ineffective (because of encryption and trusted 
-        server keys).
+        keys).
 
       o Attacks that change the contents of the data or add extra
         data to the packets are ineffective (because of encryption and
@@ -65,26 +130,4 @@ A: A good question which I don't have a answer.  SILC has been tried to
         by using the best cryptographic algorithms out there.
 
 
-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.
-
-   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.
+More to come later...
diff --git a/doc/Makefile.am.pre b/doc/Makefile.am.pre
new file mode 100644 (file)
index 0000000..aff1254
--- /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
+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..3b3d7d5
--- /dev/null
@@ -0,0 +1,1943 @@
+.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-02.txt                     XXX
+Expires: XXX
+
+.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 ................................. 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:  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.
+
+        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 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..ff32d17
--- /dev/null
@@ -0,0 +1,1112 @@
+.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-04.txt                      XXX
+Expires: XXX
+
+.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 ........................... 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.
+
+     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..e16ee17
--- /dev/null
@@ -0,0 +1,2789 @@
+.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-04.txt                           XXX
+Expires: XXX
+
+.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 .................................. 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 Resume Router Payload .............................. 43
+      2.3.22 File Transfer 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:  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
+
+
+.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.
+
+
+.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..5d13832
--- /dev/null
@@ -0,0 +1,2231 @@
+.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 XX XXXXXX 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-04.txt                         XX XXXXXXX 2001
+Expires: XXXXXXX
+
+.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 ............................................ 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
+  3.13 Backup Routers ...........................................  8
+      3.13.1 Switching to Backup Router .........................  8
+      3.13.2 Resuming Primary Router ............................  8
+      3.13.3 Discussion on Backup Router Scheme .................  8
+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 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.
+
+
+.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 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>. 
+
+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.
+
+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.  The clients should be able to trust the servers they
+are using.
+
+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, 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 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..bbd271d
--- /dev/null
@@ -0,0 +1,210 @@
+#
+# 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.
+#
+
+#
+# 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
+
+#
+# Run SILC server as specific user and group. The server must be initially
+# run as root.
+#
+# Format: <user>:<group>
+#
+[Identity]
+nobody:nobody
+
+#
+# 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:706
+
+#
+# Server keys
+#
+# Format: +<public key>:<private key>
+#
+[ServerKeys]
+@ETCDIR@/silcd.pub:@ETCDIR@/silcd.prv
+
+#
+# Listenning ports.
+#
+# Format: <local IP>:<Listener IP>:<port>
+#
+[ListenPort]
+10.2.1.6:10.2.1.6:706
+
+#
+# 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:@LOGSDIR@/silcd.log:10000
+#warninglogfile:@LOGSDIR@/silcd_warning.log:10000
+#errorlogfile:@LOGSDIR@/error.log:10000
+#fatallogfile:@LOGSDIR@/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>
+#
+# The <auth data> is either passphrase or file path to the public key
+# file.
+#
+[ClientConnection]
+:::706:1
+
+#
+# Configured server administrator connections
+#
+# Format: <host>:<username>:<nickname>:<auth method>:<auth data>
+#
+# The <auth data> is either passphrase or file path to the public key
+# file.
+#
+[AdminConnection]
+10.2.1.199:priikone:pekka:passwd:veryscret
+
+#
+# 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>:<class>:<backup connection>
+#
+# The <auth data> is either passphrase or file path to the public key
+# file. If the connection is backup connection then set the <backup 
+# connection> to value 1. For normal connections set it 0. If it is
+# set to value 1 then this server will be backup router.
+#
+[ServerConnection]
+10.2.1.7:passwd:veryscret:706:1:1:0
+10.2.1.17:passwd:veryscret13:706:1:1:1   # backup connection, that host
+                                         # will use this server as backup
+                                         # router.
+
+#
+# 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>:<initiator>:<backup replace IP>:<backup replace port>:
+#         <local backup>
+#
+# The <auth data> is either passphrase or file path to the public key
+# file. If you are the initiator of the connection then set the <initiator>
+# to value 1.  If you are the responder of the connection (waiting for 
+# incoming connection) then set it to 0.
+#
+# If the connection is backup router connection then set the <backup
+# replace IP> 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
+# <backup replace port>.  For normal connection leave both empty. If this
+# backup router is in our cell then set the <local backup> to value 1.
+# If the backup router is in other cell then set it to value 0.
+#
+[RouterConnection]
+#10.2.1.100:passwd:veryverysecret:706:1:1:1
+#10.2.100.131:pubkey:/path/to/the/publickey:706:1:1:1
+#10.2.100.100:pubkey:/path/to/the/publickey:706:1:1:0:10.2.1.6:706:1
+
+#
+# Denied connections.
+#
+# These connections are denied to connect our server.
+#
+# Format: <remote host>:<port>:<comment>
+#
+[DenyConnection]
+#10.2.1.99:0:Your connection has been denied
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..5b1734c
--- /dev/null
@@ -0,0 +1,907 @@
+<!-- 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 / 03 Aug 2001">
+</head>
+<body bgcolor="#ffffff">
+
+<font face="Helvetica">
+
+<font size="6"><b>SILC Protocol White Paper</b></font><br>
+<font size="2">Version 1.0 / 03 Aug 2001</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 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 and various commands.  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>
+
+Generally it is assumed that the SILC Network is trusted.  This means
+that clients can fully trust the servers and routers in the SILC Network.
+In real life this is not always possible.  In the Internet it is possible
+that some server or router would get compromised by a malicious
+cracker.  However, if the SILC Network is closed network, for example
+inside a orgranization the assumption generally is true.  The SILC
+protocol is secure even if the end users consider the network
+untrusted, and provides several ways to still have secure conversation
+on the SILC Network.
+<p>
+
+The packets in the SILC network are always encrypted.  It is not possible
+to send unencrypted messages in SILC.  This assures that end user cannot
+even accidently send unencrypted messages while thinking that it is
+encrypted.  This is the problem of most 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.  In these cases the security is achieved usually by encrypting the
+data while key management 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>
+
+The network topology is also different to various other chat protocol,
+like for example IRC.  IRC has tree style network where SILC has so
+called cellular network.  A cell consists of a router, servers and clients.
+The cell can also have backup routers in case the private 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 has 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 perferred 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 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.
+
+
+<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 man-in-the-middle attack.
+<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 responder also computes a signature
+that the initiator will verify.  It is also possible to perform a
+mutual authentication where both of the parties computes a signature
+which are verified by each other independently.  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><br>
+<object data="silc_priv3.jpg" type="application/postscript">
+<img src="silc_priv3.png" alt="Private Messges with Public Key Cryptosystem" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above shows the Client A has the Client B's public key.
+It will encrypt the message with that key and sends the message to the
+SILC Network.  All servers and routers pass the message through since
+they cannot decrypt it.  The Client B then uses its private key to
+decrypt the message.  The Client B has also the Client A's public key 
+that it can use to encrypt messages that it will send to Client A.
+<p>
+
+Even this method of private message delivery is not perfect.  The drawbacks
+of this method is that the public key encryption process, as being
+asymmetric cryptosystem, is much slower than encryption process with
+symmetric cryptosystems.  This is not probably problem with short messages
+but may be inconvenient with long messages.  The other drawback 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. 
+However, the servers may not have authenticated the fetched public key so
+that should not be fully trusted.  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 they trust that the keys are authentic.
+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>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-03.txt">
+Secure Internet Live Conferencing (SILC), Protocol Specification</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-pp-03.txt">
+SILC Packet Protocol</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-ke-auth-03.txt">
+SILC Key Exchange and Authentication Protocols</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-commands-01.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..29fe989
--- /dev/null
@@ -0,0 +1,2447 @@
+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 4908\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7DFF7FC07FC01FC033FF03C07FFC7FFC19FC03C033FF03C07FFC7FFC\r
+19FC03C033FF03C07FFC7FFC19FC03C033FF03C069FC06827FFC29FC03C033FF\r
+03C024FC0C8203FC3F821EFC0F8203FC068206FC038203FC038206FC038206FC\r
+038203FC098203FC0C8233FC03C033FF03C069FC038224FC038215FC038206FC\r
+038206FC098203FC068206FC09823CFC03C033FF03C07FFC7FFC19FC03C033FF\r
+03C07FFC7FFC19FC03C033FF03C069FC068224FC03820CFC038203FC038206FC\r
+038206FC038203FC098203FC098242FC03C033FF03C021FC518221FC0F820CFC\r
+098203FC068206FC098248FC03C033FF03C0098203FC038203FC038254FC0382\r
+7FFC2CFC03C033FF03C0068206FC038203FC03827FFC7FFC04FC03C033FF03C0\r
+7FFC7FFC19FC03C033FF7FC07FC01FC07FFF7FFF7FFF0203F3EDF37FC07FC010\r
+C00203F3EDF32AFFFAF3EDF3F3EDF309C0025AF3EDF309C0FAF3EDF3F3EDF321\r
+FFFDF3EDF306C00260F3EDF306C0FDF3EDF31BFF06C00264F3EDF306C015FF03\r
+C00268F3EDF303C00FFFFDF3EDF303C0022AF3EDF345820227F3EDF303C0FDF3\r
+EDF30CFF03C0022BF3EDF303820215E6F2FF03820228F3EDF303C009FFFDF3ED\r
+F303C0022BF3EDF303820215E6F2FF03820228F3EDF303C0FDF3EDF306FF03C0\r
+021DF3EDF324820203F3EDF303820215E6F2FF0382FDF3EDF306820204F3EDF3\r
+24820216F3EDF303C006FF03C0021CF3EDF30382020CF3EDF30C820215E6F2FF\r
+0C82FDF3EDF30982020CF3EDF303820216F3EDF306FF03C0021CF3EDF3038202\r
+0FF3EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2FF0382FAF3EDF3F3ED\r
+F303820210F3EDF303820216F3EDF303C003FF03C0021BF3EDF303820210F3ED\r
+F303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF03820214F3\r
+EDF303820215F3EDF303C003FF03C0021BF3EDF303820210F3EDF303820205E6\r
+F2FF06820205E6F2FF03820208E6F2FF03820214F3EDF303820215F3EDF303C0\r
+03FF03C0021BF3EDF303820210F3EDF303820215E6F2FF03820214F3EDF30382\r
+0214F3EDF303FF03C003FF03C0021BF3EDF303820210F3EDF303820215E6F2FF\r
+03820214F3EDF303820214F3EDF303FF03C003FF03C0021BF3EDF303820210F3\r
+EDF303820215E6F2FF03820214F3EDF303820214F3EDF303FF03C003FF03C002\r
+1BF3EDF303820210F3EDF345820214F3EDF303820214F3EDF303FF03C003FF03\r
+C0021AF3EDF309820239F3EDF303820215F3EDF303FF03C003FF03C0021BF3ED\r
+F306820239F3EDF303820215F3EDF303FF03C003FF03C0021BF3EDF30382023A\r
+F3EDF303820215F3EDF303FF03C003FF03C0020EF3EDF348820225F3EDF34582\r
+020AF3EDF303FF03C003FF03C0020EF3EDF303820216E6F2FF03820225F3EDF3\r
+03820215E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820216E6\r
+F2FF03820225F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C002\r
+0EF3EDF303820216E6F2FF03820225F3EDF303820215E6F2FF0382020AF3EDF3\r
+03FF03C003FF03C0020EF3EDF303820216E6F2FF03820225F3EDF303820215E6\r
+F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820205E6F2FF0382FD\r
+E6F2FF1B820206E6F2FF0382FDF3EDF306820204F3EDF30382FDF3EDF30F82FA\r
+F3EDF3F3EDF303820214F3EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2\r
+FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820205E6F2FF0682FDE6\r
+F2FF0982FDE6F2FF0C820206E6F2FF0F82020FF3EDF306820210F3EDF3038202\r
+05E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF0382020AF3EDF303FF\r
+03C003FF03C0020EF3EDF303820205E6F2FF06820205E6F2FF03820209E6F2FF\r
+0382FAF3EDF3F3EDF303820212F3EDF30382020FF3EDF303820205E6F2FF0682\r
+0205E6F2FF03820208E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF3\r
+03820216E6F2FF03820216F3EDF30382020EF3EDF303820215E6F2FF0382020A\r
+F3EDF303FF03C003FF03C0020EF3EDF303820216E6F2FF03820217F3EDF30382\r
+020DF3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF3\r
+03820216E6F2FF03820219F3EDF30382020BF3EDF303820215E6F2FF0382020A\r
+F3EDF303FF03C003FF03C0020EF3EDF348820219F3EDF30382020BF3EDF34582\r
+020AF3EDF303FF03C003FF03C00216F3EDF30382020AF3EDF30982021CF3EDF3\r
+03820217F3EDF306820212F3EDF303FF03C003FF03C00215F3EDF30382020CF3\r
+EDF30982021CF3EDF303820216F3EDF309820211F3EDF303FF03C003FF03C002\r
+13F3EDF30982020FF3EDF30382021CF3EDF303820216F3EDF303820212F3EDF3\r
+03FF03C003FF03C00213F3EDF30382030003820210F3EDF30382021CF3EDF303\r
+820216F3EDF303820211F3EDF303FF03C003FF03C00213F3EDF306820212F3ED\r
+F30382021CF3EDF303820215F3EDF303820211F3EDF303FF03C003FF03C00213\r
+F3EDF303820214F3EDF30382FDF3EDF30382021AF3EDF303820214F3EDF30382\r
+0211F3EDF303FF03C003FF03C00204F3EDF345820210F3EDF30382021AF3EDF3\r
+0382020AF3EDF345820204F3EDF303FF03C003FF03C00204F3EDF303820215E6\r
+F2FF03820211F3EDF303820219F3EDF30382020AF3EDF303820215E6F2FF0382\r
+0204F3EDF303FF03C003FF03C00204F3EDF303820215E6F2FF03820212F3EDF3\r
+06820218F3EDF303820209F3EDF303820215E6F2FF03820204F3EDF303FF03C0\r
+03FF03C00204F3EDF303820215E6F2FF03820214F3EDF303820221F3EDF30382\r
+0215E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF303820205E6F2FF\r
+0382FDE6F2FF1B820205E6F2FF03820214F3EDF306820218F3EDF303820207F3\r
+EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2FF03820204F3EDF303FF03\r
+C003FF03C00204F3EDF303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C8202\r
+05E6F2FF0382022FF3EDF303820206F3EDF303820205E6F2FF0682FDE6F2FF09\r
+82FDE6F2FF0C820205E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF3\r
+03820205E6F2FF06820205E6F2FF03820208E6F2FF03820225F3EDF312820203\r
+F3EDF31B820205E6F2FF06820205E6F2FF03820208E6F2FF03820204F3EDF303\r
+FF03C003FF03C00204F3EDF303820215E6F2FF03820217F3EDF303820209F3ED\r
+F303820203F3EDF30382020DF3EDF30682FDF3EDF303820215E6F2FF03820204\r
+F3EDF303FF03C003FF03C00204F3EDF303820215E6F2FF03820217F3EDF30382\r
+0208F3EDF303820212F3EDF30382FAF3EDF3F3EDF303820215E6F2FF03820204\r
+F3EDF303FF03C003FF03C00204F3EDF303820215E6F2FF03820218F3EDF30382\r
+0218F3EDF303820204F3EDF303820215E6F2FF03820204F3EDF303FF03C003FF\r
+03C00204F3EDF303820215E6F2FF03820218F3EDF303820206F3EDF303820212\r
+F3EDF303820203F3EDF303820215E6F2FF03820204F3EDF303FF03C003FF03C0\r
+0204F3EDF345820219F3EDF303820205F3EDF303820216F3EDF345820204F3ED\r
+F303FF03C003FF03C00213F3EDF303820220F3EDF303820204F3EDF303820214\r
+F3EDF30382020BF3EDF306820210F3EDF303FF03C003FF03C00213F3EDF30382\r
+0220F3EDF303820204F3EDF303820215F3EDF303820209F3EDF309820210F3ED\r
+F303FF03C003FF03C00214F3EDF30682021EF3EDF303820203F3EDF303820216\r
+F3EDF30382020AF3EDF303820211F3EDF303FF03C003FF03C00213F3EDF30382\r
+03000382021EF3EDF303820203F3EDF303820218F3EDF303820208F3EDF30382\r
+0211F3EDF303FF03C003FF03C00214F3EDF306820222F3EDF303820220F3EDF3\r
+03820212F3EDF303FF03C003FF03C00215F3EDF30382023CF3EDF303820206F3\r
+EDF303820212F3EDF303FF03C003FF03C0020DF3EDF345820227F3EDF3458202\r
+0AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820215E6F2FF0382020F\r
+F3EDF303820217F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C0\r
+020CF3EDF3FDE6F2FF03820215E6F2FF03820214F3EDF303820212F3EDF30382\r
+0215E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF038202\r
+15E6F2FF0382020EF3EDF303820205F3EDF303820212F3EDF303820215E6F2FF\r
+0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820205E6F2FF03\r
+82FDE6F2FF1B820205E6F2FF0382020EF3EDF303820205F3EDF303820212F3ED\r
+F303820205E6F2FF0382FDE6F2FF1B820205E6F2FF0382020AF3EDF303FF03C0\r
+03FF03C0020CF3EDF3FDE6F2FF03820205E6F2FF0682FDE6F2FF0982FDE6F2FF\r
+0C820205E6F2FF0382020EF3EDF303820205F3EDF303820212F3EDF303820205\r
+E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF0382020AF3EDF303FF03\r
+C003FF03C0020CF3EDF3FDE6F2FF03820205E6F2FF06820205E6F2FF03820208\r
+E6F2FF0382020DF3EDF303820206F3EDF303820212F3EDF303820205E6F2FF06\r
+820205E6F2FF03820208E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3ED\r
+F3FDE6F2FF03820215E6F2FF0382020DF3EDF303820206F3EDF303820212F3ED\r
+F303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF\r
+03820215E6F2FF0382020DF3EDF303820219F3EDF303820215E6F2FF0382020A\r
+F3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820215E6F2FF0382020DF3\r
+EDF303820219F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C002\r
+0CF3EDF3FDE6F2FF03820215E6F2FF0382020CF3EDF30382021AF3EDF3038202\r
+15E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF45820214\r
+F3EDF303820212F3EDF34582020AF3EDF303FF03C003FF03C00218F3EDF30382\r
+023DF3EDF303820215F3EDF303FF03C003FF03C00217F3EDF303820220F3EDF3\r
+0382021CF3EDF306820215F3EDF303FF03C003FF03C00217F3EDF303820215F3\r
+EDF342820214F3EDF303820214F3EDF303FF03C003FF03C00217F3EDF3038202\r
+14F3EDF345820214F3EDF303820214F3EDF303FF03C003FF03C00217F3EDF303\r
+820214F3EDF303820215E6F2FF03820214F3EDF303820214F3EDF303FF03C003\r
+FF03C00217F3EDF303820214F3EDF303820215E6F2FF03820213F3EDF3038202\r
+15F3EDF303FF03C003FF03C00218F3EDF30982020EF3EDF30682FDF3EDF30382\r
+0215E6F2FF0382020FF3EDF30F820215F3EDF303FF03C003FF03C0021BF3EDF3\r
+36820205E6F2FF0382FDE6F2FF1B820205E6F2FF3082021AF3EDF303FF03C003\r
+FF03C00229F3EDF30382FAF3EDF3F3EDF303820205E6F2FF0682FDE6F2FF0982\r
+FDE6F2FF0C820205E6F2FF03820229F3EDF303FF03C003FF03C0022CF3EDF303\r
+820205E6F2FF06820205E6F2FF03820208E6F2FF03820229F3EDF303FF03C003\r
+FF03C0022CF3EDF303820215E6F2FF03820229F3EDF303FF03C003FF03C0022C\r
+F3EDF303820215E6F2FF03820229F3EDF303FF03C003FF03C0022CF3EDF30382\r
+0215E6F2FF03820229F3EDF303FF03C003FF03C0022CF3EDF345820229F3EDF3\r
+03FF03C003FF03C0022CF3EDF30216E6F2FF022AF3EDF303FF03C003FF03C002\r
+6CF3EDF303FF03C003FF03C0026DF3EDF303C003FF03C0026CF3EDF306C003FF\r
+03C00217F3EDF303C0FDF3EDF306C0FDF3EDF312C0FDF3EDF303C0FAF3EDF3F3\r
+EDF309C00206F3EDF309C0FAF3EDF3F3EDF30CC0FAF3EDF3F3EDF306C0FAF3ED\r
+F3F3EDF303C0FDF3EDF306C0FAF3EDF3F3EDF306C0FDF3EDF303C0FDF3EDF303\r
+C0FAF3EDF3F3EDF30CC0FAF3EDF3F3EDF30CC00216F3EDF303C006FF03C00217\r
+F3EDF303C0FDF3EDF303C0FDF3EDF303C0FAF3EDF3F3EDF303C0FAF3EDF3F3ED\r
+F303C0FDF3EDF303C0FDF3EDF303C0FAF3EDF3F3EDF303C00205F3EDF306C0FA\r
+F3EDF3F3EDF306C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF306C0FAF3EDF3F3ED\r
+F306C00203F3EDF303C00203F3EDF303C0FDF3EDF303C0FDF3EDF303C0FAF3ED\r
+F3F3EDF306C0FAF3EDF3F3EDF303C0FDF3EDF309C00215F3EDF303C006FF03C0\r
+0217F3EDF309C0FDF3EDF303C0FAF3EDF3F3EDF303C0FAF3EDF3F3EDF303C0FD\r
+F3EDF303C0FDF3EDF303C0FAF3EDF3F3EDF306C00204F3EDF303C00204F3EDF3\r
+03C0FAF3EDF3F3EDF309C0FDF3EDF309C0FDF3EDF306C0FAF3EDF3F3EDF306C0\r
+0203F3EDF303C0FDF3EDF303C0FDF3EDF303C0FAF3EDF3F3EDF309C0FDF3EDF3\r
+06C00218F3EDF303C009FF03C00216F3EDF303C0FAF3EDF3F3EDF303C0FDF3ED\r
+F309C0FAF3EDF3F3EDF309C0FAF3EDF3F3EDF309C0FDF3EDF303C00203F3EDF3\r
+06C00204F3EDF306C0FDF3EDF303C0FDF3EDF303C0FDF3EDF303C0FDF3EDF303\r
+C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF30CC0FDF3EDF303C0FAF3EDF3F3EDF3\r
+06C0FDF3EDF303C0FDF3EDF303C0FAF3EDF3F3EDF306C00215F3EDF303C00CFF\r
+03C00216F3EDF309C00209F3EDF303C0020BF3EDF309C00215F3EDF303C0FDF3\r
+EDF303C00221F3EDF303C00FFF03C00268F3EDF303C012FF03C00268F3EDF303\r
+C015FF06C00264F3EDF306C01EFF06C00260F3EDF306C02AFF7FC07FC019C0FD\r
+F3EDF306C018FFFE00000000\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
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..cd5ecd16d3a5d7f8785f5944c6e60ef9e6f63040 100644 (file)
@@ -21,4 +21,15 @@ 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
+endif
+
+EXTRA_DIST = \
+       silcdefs.h.in *.h
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
index abb8b3f87a31e49af30b0f5d22d875a2b50e6381..4484d57675950b4f0f7f1e5be438c644379673bc 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
+
+#ifdef SILC_WIN32
+#include "silcwin32.h"
+#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 +59,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
+
+#endif                         /* !SILC_WIN32 */
+
+#ifndef HAVE_GETOPT_LONG
+#include "../lib/contrib/getopt.h"
+#endif
+
 #ifndef TRUE
 #define TRUE 1
 #endif
 #define FALSE 0
 #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 bool
+#define bool unsigned char
+#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 "silchashtable.h"
 #include "silclog.h"
 #include "silcmemory.h"
 #include "silcbuffer.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 "silcpayload.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"
+
+/* TRQ (SilcList API and SilcDList API) */
+#include "silclist.h"
+#include "silcdlist.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
similarity index 52%
rename from lib/silccrypt/e2_internal.h
rename to includes/silcwin32.h
index 446b0a1bf21008142cde87aa587ee7efe9af770c..7871654e58a4761509b2f54b69c051c0c748fa07 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  e2_internal.h
+  silcwin32.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  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
   GNU General Public License for more details.
 
 */
+/* Native WIN32 specific includes and definitions. */
 
-#ifndef E2_INTERNAL_H
-#define E2_INTERNAL_H
+#ifndef SILCWIN32_H
+#define SILCWIN32_H
 
-typedef struct {
-  u4byte l_key[72];
-} E2Context;
+#include <windows.h>
+#include <io.h>
+#include <process.h>
 
-/* 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]);
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+#ifdef WIN32
+#define strcasecmp strcmp
+#define strncasecmp strncmp
+#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/LIBINDEX b/lib/LIBINDEX
new file mode 100644 (file)
index 0000000..9c0073a
--- /dev/null
@@ -0,0 +1,15 @@
+<!--
+ Index file for SILC Toolkit Reference Manual. This file is processed with
+ the SILC Document generator.
+-->
+
+<FONT SIZE="+3" COLOR="#000044"><B>SILC Toolkit Reference Manual</B></FONT>
+<BR>
+Copyright (C) GNU GPL 2001 The SILC Project<BR>
+Updated: @DATE@
+<BR><BR>
+<B><FONT SIZE="2">Note that this document is still under work and does not
+include yet all references for SILC Toolkit interfaces.  Consider this to
+be version 0.6 of the SILC Toolkit Reference Manual.</FONT></B>
+<BR><BR>
+@BODY@
diff --git a/lib/Makefile.am.pre b/lib/Makefile.am.pre
new file mode 100644 (file)
index 0000000..83a39e0
--- /dev/null
@@ -0,0 +1,97 @@
+#
+#  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 \
+       dotconf \
+       trq
+#        zlib
+
+SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+DIST_SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+
+# SILC Library dirs
+SILCLIB_DIRS = \
+       contrib \
+       silccore \
+       silccrypt \
+       silcsim \
+       silcmath \
+       silcske \
+       silcutil \
+       silcsftp \
+       trq \
+       dotconf
+
+# 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_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
+
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..1378a22
--- /dev/null
@@ -0,0 +1,747 @@
+/* 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>
+
+/* 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;
+
+      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/silcclient/DIRECTORY b/lib/silcclient/DIRECTORY
new file mode 100644 (file)
index 0000000..f4ac255
--- /dev/null
@@ -0,0 +1,29 @@
+<!--
+@LIBRARY=SILC Client Library
+@FILENAME=silcclientlib.html
+@LINK=silcapi.html:SILC Client API
+-->
+
+<FONT SIZE="+3">SILC Client Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+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.
+
+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.
+</FONT>
+</PRE>
+
+@LINKS@
similarity index 56%
rename from doc/Makefile.am
rename to lib/silcclient/Makefile.am
index 7f6fea86104f263e89122d107e72af8270e24e13..29ab9c5a244ddbb01157e13fd32d314921bdeac7 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
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcclient/README b/lib/silcclient/README
new file mode 100644 (file)
index 0000000..4295f27
--- /dev/null
@@ -0,0 +1,224 @@
+
+                      SILC Client Library Manual
+
+                             Version 0.5
+
+1.0 Introduction
+
+SILC Client library is a full featured SILC Client protocolimplementation.
+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.
+
+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.
+
+
+1.1 Creating Client
+
+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.
+
+The client object is SilcClient which is usually allocated in following
+manner:
+
+       SilcClient client = silc_client_alloc(&ops, context, version);
+
+`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.
+
+`ops' can be defined for example as follows:
+
+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,
+};
+
+
+1.2 Initializing the Client
+
+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:
+
+       client->username
+       client->hostname
+       client->realname
+       client->pkcs
+       client->public_key
+       client->private_key
+
+After setting the pointers one must call:
+
+       silc_client_init(client);
+
+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.
+
+
+1.3 Running the Client
+
+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:
+
+       silc_client_run(client);
+
+Usually application may do some other initializations before calling
+this function.  For example before calling this function application
+should initialize the user interface.
+
+
+1.4 Creating Connection to Server
+
+Connection to remote SILC server is done by calling:
+
+       silc_client_connect_to_server(client, port, hostname, context);
+
+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.
+
+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).
+
+
+1.4.1 Using Own Connecting
+
+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.
+
+After connection has been created application must call:
+
+       SilcClientConnection conn;
+
+       /* Add new connection to client */
+       conn = silc_client_add_connection(client, hostname, port, context);
+
+       /* Start key exchange and let the library handle everything
+          after this point on. */
+       silc_client_start_key_exchange(client, conn, sock);
+
+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.
+
+
+1.5 Example Client
+
+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.
+
+#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;
+}
diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c
new file mode 100644 (file)
index 0000000..7ff149f
--- /dev/null
@@ -0,0 +1,1770 @@
+/*
+
+  client.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 "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->ops = ops;
+  new_client->silc_client_version = strdup(silc_version);
+  new_client->params = silc_calloc(1, sizeof(*new_client->params));
+
+  if (params)
+    memcpy(new_client->params, params, sizeof(*params));
+
+  if (!new_client->params->task_max)
+    new_client->params->task_max = 200;
+
+  if (!new_client->params->rekey_secs)
+    new_client->params->rekey_secs = 3600;
+
+  if (!new_client->params->connauth_request_secs)
+    new_client->params->connauth_request_secs = 2;
+
+  new_client->params->
+    nickname_format[sizeof(new_client->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->silc_client_version);
+    silc_free(client->params);
+    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->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_rng_global_init(client->rng);
+
+  /* Register protocols */
+  silc_client_protocols_register();
+
+  /* Initialize the scheduler */
+  client->schedule = silc_schedule_init(client->params->task_max ?
+                                       client->params->task_max : 200);
+  if (!client->schedule)
+    return FALSE;
+
+  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_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);
+}
+
+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->conns_count; i++)
+    if (client->conns && !client->conns[i]) {
+      client->conns[i] = conn;
+      return conn;
+    }
+
+  client->conns = silc_realloc(client->conns, sizeof(*client->conns)
+                              * (client->conns_count + 1));
+  client->conns[client->conns_count] = conn;
+  client->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->conns_count; i++)
+    if (client->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->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->sockets) {
+    client->sockets = silc_calloc(1, sizeof(*client->sockets));
+    client->sockets[0] = silc_socket_dup(sock);
+    client->sockets_count = 1;
+    return;
+  }
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == NULL) {
+      client->sockets[i] = silc_socket_dup(sock);
+      return;
+    }
+  }
+
+  client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
+                                (client->sockets_count + 1));
+  client->sockets[client->sockets_count] = silc_socket_dup(sock);
+  client->sockets_count++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->sockets)
+    return;
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == sock) {
+      silc_socket_free(sock);
+      client->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->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;
+}
+
+/* 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. */
+
+bool silc_client_start_key_exchange(SilcClient client,
+                                   SilcClientConnection conn,
+                                   int fd)
+{
+  SilcProtocol protocol;
+  SilcClientKEInternalContext *proto_ctx;
+  void *context;
+
+  /* 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 = strdup(client->username);
+  conn->sock->hostname = conn->remote_host;
+  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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                    "Error: Could not start key exchange protocol");
+    return FALSE;
+  }
+  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(fd);
+
+  /* Execute the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+  return TRUE;
+}
+
+/* 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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to server %s: %s",
+                      ctx->host, strerror(opt));
+      client->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->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->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);
+
+  if (!silc_client_start_key_exchange(client, conn, fd)) {
+    silc_net_close_connection(fd);
+    client->ops->connect(client, conn, FALSE);
+  }
+}
+
+/* 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 */
+    client->ops->connect(client, ctx->sock->user_data, FALSE);
+    silc_free(ctx);
+    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->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->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 */
+    client->ops->connect(client, ctx->sock->user_data, FALSE);
+    silc_free(ctx);
+    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->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->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->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->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 (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);
+       
+       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);
+      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_del_all(conn->client_cache);
+    if (conn->channel_cache)
+      silc_idcache_del_all(conn->channel_cache);
+    if (conn->server_cache)
+      silc_idcache_del_all(conn->server_cache);
+
+    /* Free data (my ID is freed in above silc_client_del_client) */
+    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->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->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;
+  SilcBuffer sidp;
+
+  if (!conn->local_entry)
+    connecting = TRUE;
+
+  /* Delete old ID from ID cache */
+  if (conn->local_id) {
+    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 = silc_id_payload_get_id(idp);
+  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;
+  
+  /* Put it to the ID cache */
+  silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
+                  (void *)conn->local_entry, FALSE);
+
+  /* Issue INFO command to fetch the real server name and server information
+     and other stuff. */
+  sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+  silc_client_send_command(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. */
+  if (connecting)
+    client->ops->connect(client, conn, TRUE);
+}
+
+/* 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. 
+   Returns the created channel entry. This is also called when received
+   channel ID in for example USERS command reply that we do not have. */
+
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+                                           SilcSocketConnection sock,
+                                           char *channel_name,
+                                           uint32 mode, 
+                                           SilcIDPayload idp)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("New channel ID"));
+
+  channel = silc_calloc(1, sizeof(*channel));
+  channel->channel_name = channel_name;
+  channel->id = silc_id_payload_get_id(idp);
+  channel->mode = mode;
+  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
+
+  /* Put it to the ID cache */
+  silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                  (void *)channel->id, (void *)channel, FALSE);
+
+  return channel;
+}
+
+/* Removes a client entry from all channel it has joined. This really is
+   a performance killer (client_entry should have pointers to channel 
+   entry list). */
+
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+
+  if (!silc_idcache_get_all(conn->channel_cache, &list))
+    return;
+
+  silc_idcache_list_first(list, &id_cache);
+  channel = (SilcChannelEntry)id_cache->context;
+  
+  while (channel) {
+    
+    /* Remove client from channel */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       silc_list_del(channel->clients, chu);
+       silc_free(chu);
+       break;
+      }
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+    
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  silc_idcache_list_free(list);
+}
+
+/* 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)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+
+  if (!silc_idcache_get_all(conn->channel_cache, &list))
+    return;
+
+  silc_idcache_list_first(list, &id_cache);
+  channel = (SilcChannelEntry)id_cache->context;
+  
+  while (channel) {
+    
+    /* Replace client entry */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == old) {
+       chu->client = new;
+       break;
+      }
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+    
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  silc_idcache_list_free(list);
+}
+
+/* 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->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->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..d8bee7a
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+
+  client.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.
+
+*/
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+/* Forward declarations */
+typedef struct SilcClientStruct *SilcClient;
+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 {
+  /*
+   * Public data. All the following pointers must be set by the allocator
+   * of this structure.
+   */
+
+  /* Users's username, hostname and realname. */
+  char *username;
+  char *hostname;
+  char *realname;
+
+  /* Private and public key of the user. */
+  SilcPKCS pkcs;
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+
+  /* Application specific user data pointer. Client library does not
+     touch this. */
+  void *application;
+
+  /*
+   * Private data. Following pointers are used internally by the client
+   * library and should be considered read-only fields.
+   */
+
+  /* All client operations that are implemented in the application. */
+  SilcClientOperations *ops;
+
+  /* Client Parameters */
+  SilcClientParams *params;
+
+  /* SILC client scheduler */
+  SilcSchedule schedule;
+
+  /* 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;
+
+  /* Generic cipher and hash objects. These can be used and referenced
+     by the application as well. */
+  SilcCipher none_cipher;
+  SilcHash md5hash;
+  SilcHash sha1hash;
+  SilcHmac md5hmac;
+  SilcHmac sha1hmac;
+
+  /* Random Number Generator. Application should use this as its primary
+     random number generator. */
+  SilcRng rng;
+
+  /* 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)->conns_count; __i++)       \
+    if ((__x)->conns[__i] &&                           \
+       (__x)->conns[__i]->sock->sock == (__fd))        \
+      break;                                           \
+                                                       \
+  if (__i >= (__x)->conns_count) {                     \
+    (__sock) = NULL;                                   \
+    for (__i = 0; __i < (__x)->sockets_count; __i++)   \
+      if ((__x)->sockets[__i] &&                       \
+         (__x)->sockets[__i]->sock == (__fd))          \
+        (__sock) = (__x)->sockets[__i];                        \
+  } else                                               \
+    (__sock) = (__x)->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 (some of the prototypes are defined in the silcapi.h) */
+
+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);
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+                                           SilcSocketConnection sock,
+                                           char *channel_name,
+                                           uint32 mode, 
+                                           SilcIDPayload idp);
+void silc_client_save_channel_key(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_channel.c b/lib/silcclient/client_channel.c
new file mode 100644 (file)
index 0000000..440c699
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+
+  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->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);
+}
+
+static void silc_client_channel_message_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcClientEntry *clients,
+                                          uint32 clients_count,
+                                          void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+
+  if (clients)
+    silc_client_channel_message(client, conn->sock, packet);
+  silc_packet_context_free(packet);
+}
+
+/* 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;
+  SilcChannelUser chu;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientID *client_id = NULL;
+  bool found = FALSE;
+  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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id, &id_cache))
+    goto out;
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* 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, 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, 
+                                                  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, entry->cipher,
+                                                  entry->hmac);
+      if (payload)
+       break;
+    }
+    if (entry == SILC_LIST_END)
+      goto out;
+  } else {
+    goto out;
+  }
+
+  /* Find client entry */
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (SILC_ID_CLIENT_COMPARE(chu->client->id, client_id) && 
+       chu->client->nickname) {
+      found = TRUE;
+      break;
+    }
+  }
+
+  if (!found) {
+    /* Resolve the client info */
+    silc_client_get_client_by_id_resolve(client, conn, client_id,
+                                        silc_client_channel_message_cb,
+                                        silc_packet_context_dup(packet));
+    goto out;
+  }
+
+  message = silc_channel_message_get_data(payload, NULL);
+
+  /* Pass the message to application */
+  client->ops->channel_message(client, conn, chu->client, channel,
+                              silc_channel_message_get_flags(payload),
+                              message);
+
+ out:
+  if (id)
+    silc_free(id);
+  if (client_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(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel)
+{
+  unsigned char *id_string, *key, *cipher, *hmac, hash[32];
+  uint32 tmp_len;
+  SilcChannelID *id;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelKeyPayload payload;
+
+  payload = silc_channel_key_payload_parse(key_payload);
+  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) {
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, 
+                                    (void *)id, &id_cache))
+      goto out;
+    
+    /* Get channel entry */
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  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(conn->client->schedule, channel->rekey_task);
+  channel->old_channel_key = channel->channel_key;
+  channel->old_hmac = channel->hmac;
+  channel->rekey_task = 
+    silc_schedule_task_add(conn->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)) {
+    conn->client->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(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->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);
+}
diff --git a/lib/silcclient/client_ftp.c b/lib/silcclient/client_ftp.c
new file mode 100644 (file)
index 0000000..7f85a8d
--- /dev/null
@@ -0,0 +1,1123 @@
+/*
+
+  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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to client %s: %s",
+                      ctx->host, strerror(opt));
+      client->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->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->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) {
+    /* 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.
+
+   This returns a file session ID for the file transmission.  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. This returns 0 if the
+   file indicated by the `filepath' is being transmitted to the remote
+   client indicated by the `client_entry', already. */
+
+uint32 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)
+{
+  SilcClientFtpSession session;
+  SilcBuffer keyagr, ftp;
+  char *filename, *path;
+
+  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 (!strcmp(session->filepath, filepath) && 
+       session->client_entry == client_entry)
+      return 0;
+  }
+
+  /* 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;
+  } 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);
+
+  return session->session_id;
+}
+
+/* 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->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);
+  if (!payload)
+    goto out;
+
+  hostname = silc_key_agreement_get_hostname(payload);
+  port = silc_key_agreement_get_port(payload);
+
+  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->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..c9b950d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+
+  client_internal.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 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;
+};
+
+/* Protypes */
+
+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);
+
+#endif
diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c
new file mode 100644 (file)
index 0000000..d584bae
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+
+  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->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->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;
+
+  assert(client_entry);
+
+  if (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->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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to client %s: %s",
+                      ctx->host, strerror(opt));
+      client->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->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"));
+
+  assert(client_entry && hostname && port);
+
+  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"));
+
+  assert(client_entry);
+
+  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)
+{
+  assert(client_entry);
+
+  if (client_entry->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);
+    }
+    client_entry->ke = NULL;
+    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);
+    silc_free(client_entry->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);
+  if (!payload)
+    goto out;
+
+  /* Call the key_agreement client operation */
+  ret = client->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..5b508e5
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+
+  client_notify.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$ */
+/* 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;
+
+  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) {
+      silc_socket_free(res->sock);
+      return;
+    }
+  }
+
+  silc_client_notify_by_server(res->context, res->sock, res->packet);
+  silc_socket_free(res->sock);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
+  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_send_command(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_destructor,
+                             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;
+  SilcIDCacheEntry id_cache = NULL;
+  unsigned char *tmp;
+  uint32 tmp_len, mode;
+
+  payload = silc_notify_payload_parse(buffer);
+  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->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.
+     */
+    
+    /* 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 = NULL;
+    if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                   &id_cache))
+      channel = (SilcChannelEntry)id_cache->context;
+
+    /* 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->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.
+     */
+
+    /* 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;
+    }
+
+    /* 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 */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Add client to channel */
+    if (client_entry != conn->local_entry) {
+      chu = silc_calloc(1, sizeof(*chu));
+      chu->client = client_entry;
+      silc_list_add(channel->clients, 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->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.
+     */
+    
+    /* 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;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Remove client from channel */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       silc_list_del(channel->clients, chu);
+       silc_free(chu);
+       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->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.
+     */
+
+    /* 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->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.
+     */
+
+    /* 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 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;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* 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->ops->notify(client, conn, type, client_entry, tmp, channel);
+    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.
+     */
+
+    /* 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) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* 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->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
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    idp = silc_id_payload_parse_data(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;
+    }
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    &id_cache)) {
+      silc_id_payload_free(idp);
+      goto out;
+    }
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* 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->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
+     */
+
+    /* 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;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Save the mode */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       chu->mode = mode;
+       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->ops->notify(client, conn, type, client_entry, mode, 
+                       client_entry2, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /*
+     * Received Message of the day
+     */
+
+    /* Get motd */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    
+    /* Notify application */
+    client->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.
+     */
+
+    /* 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 */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
+
+    /* Free the old ID */
+    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;
+
+    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
+
+    /* Remove the old cache entry and create a new one */
+    silc_idcache_del_by_context(conn->channel_cache, channel);
+    silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                    channel->id, channel, FALSE);
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, channel, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /*
+     * A client (maybe me) was kicked from a channel
+     */
+
+    /* 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;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* 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->ops->notify(client, conn, type, client_entry, tmp, 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.
+     */
+
+    /* 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->ops->notify(client, conn, type, client_entry, tmp);
+
+    if (client_entry != conn->local_entry) {
+      /* Remove client from all channels */
+      silc_client_remove_from_channels(client, conn, client_entry);
+      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;
+
+      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->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;
+
+       silc_client_remove_from_channels(client, conn, client_entry);
+       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_prvmsg.c b/lib/silcclient/client_prvmsg.c
new file mode 100644 (file)
index 0000000..5acc861
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+
+  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;
+  SilcIDCacheEntry id_cache = 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 */
+  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)remote_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache) || 
+      ((SilcClientEntry)id_cache->context)->nickname == NULL) {
+
+    if (id_cache && id_cache->context) {
+      remote_client = (SilcClientEntry)id_cache->context;
+      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;
+  }
+
+  remote_client = (SilcClientEntry)id_cache->context;
+
+  /* Parse the payload and decrypt it also if private message key is set */
+  payload = silc_private_message_payload_parse(packet->buffer,
+                                              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->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->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->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..bdb818f
--- /dev/null
@@ -0,0 +1,2206 @@
+/*
+
+  command.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 "clientlibincludes.h"
+#include "client_internal.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, 3),
+  SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
+  SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(kill, KILL, "KILL", 
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
+  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, 3),
+  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, 5),
+  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, 4),
+  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
+  SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
+  SILC_CLIENT_CMD(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
+  SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
+  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
+  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
+  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", SILC_CF_LAG | SILC_CF_REG, 2),
+
+  { NULL, 0, NULL, 0, 0 },
+};
+
+#define SILC_NOT_CONNECTED(x, c) \
+  x->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->ops->command(cmd->client, cmd->conn, \
+  cmd, TRUE, cmd->command->cmd)
+
+/* Error to application. Usage: COMMAND_ERROR; */
+#define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
+  cmd, FALSE, cmd->command->cmd)
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct form and in correct order. */
+
+void silc_client_send_command(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(const char *name)
+{
+  SilcClientCommand *cmd;
+
+  for (cmd = silc_command_list; cmd->name; cmd++) {
+    if (!strcmp(cmd->name, name))
+      return cmd;
+  }
+
+  return 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,
+                                SilcClientPendingDestructor destructor,
+                                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;
+  reply->destructor = destructor;
+  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->destructor = r->destructor;
+      ctx->ident = ident;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Allocate Command Context */
+
+SilcClientCommandContext silc_client_command_alloc()
+{
+  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;
+}
+
+/* Pending command destructor. */
+
+static void silc_client_command_destructor(void *context)
+{
+  silc_client_command_free((SilcClientCommandContext)context);
+}
+
+/* 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_send_command(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) {
+    cmd->client->ops->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. */
+
+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);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2 || cmd->argc > 3) {
+    COMMAND_ERROR;
+    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);
+
+  /* Notify application */
+  COMMAND;
+
+ 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;
+  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->client->ops->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) {
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "Your nickname is %s on server %s", 
+                           conn->nickname, conn->remote_host);
+    } else {
+      cmd->client->ops->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;
+
+  /* Set new nickname */
+  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);
+  if (conn->nickname)
+    silc_free(conn->nickname);
+  conn->nickname = strdup(cmd->argv[1]);
+
+  /* Notify application */
+  COMMAND;
+
+ 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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+    cmd->client->ops->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)) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+      cmd->client->ops->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->params->nickname_parse)
+       client->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;
+       }
+       silc_free(nickname);
+      
+       /* 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_destructor,
+                                   silc_client_command_invite, 
+                                   silc_client_command_dup(cmd));
+       cmd->pending = 1;
+       return;
+      }
+    } 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->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->params->nickname_parse)
+    client->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) {
+    silc_client_remove_from_channels(client, conn, target);
+    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) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /KILL <nickname> [<comment>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Parse the typed nickname. */
+  if (client->params->nickname_parse)
+    client->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;
+    }
+
+    silc_free(nickname);
+
+    /* 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_destructor,
+                               silc_client_command_kill, 
+                               silc_client_command_dup(cmd));
+    cmd->pending = 1;
+    return;
+  }
+
+  /* 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,
+                             NULL, 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);
+      conn->ping_count++;
+      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;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcBuffer buffer, idp;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* See if we have joined to the requested channel already */
+  if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
+                                   &id_cache)) {
+    SilcChannelEntry channel = (SilcChannelEntry)id_cache->context;
+    if (channel->on_channel)
+      goto out;
+  }
+
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
+  if (cmd->argv_lens[1] > 256)
+    cmd->argv_lens[1] = 256;
+
+  /* Send JOIN command to the server */
+  if (cmd->argc == 2)
+    buffer = 
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
+                                    1, cmd->argv[1], cmd->argv_lens[1],
+                                    2, idp->data, idp->len);
+  else if (cmd->argc == 3)
+    /* XXX Buggy */
+    buffer = 
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
+                                    1, cmd->argv[1], cmd->argv_lens[1],
+                                    2, idp->data, idp->len,
+                                    3, cmd->argv[2], cmd->argv_lens[2]);
+  else
+    buffer = 
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
+                                    1, cmd->argv[1], cmd->argv_lens[1],
+                                    2, idp->data, idp->len,
+                                    3, cmd->argv[2], cmd->argv_lens[2],
+                                    4, cmd->argv[3], cmd->argv_lens[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);
+
+  /* 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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+      cmd->client->ops->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) {
+         cmd->client->ops->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) {
+         cmd->client->ops->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) {
+         cmd->client->ops->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) {
+         cmd->client->ops->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) {
+         cmd->client->ops->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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are on that channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  /* Parse the typed nickname. */
+  if (client->params->nickname_parse)
+    client->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;
+    }
+
+    silc_free(nickname);
+
+    /* 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_destructor,
+                               silc_client_command_cumode, 
+                               silc_client_command_dup(cmd));
+    cmd->pending = 1;
+    return;
+  }
+  
+  /* Get the current mode */
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (chu->client == client_entry) {
+      mode = chu->mode;
+      break;
+    }
+  }
+
+  /* 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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+    cmd->client->ops->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)) {
+    cmd->client->ops->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->params->nickname_parse)
+    client->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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /OPER <username> [-pubkey]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    /* Get passphrase */
+    cmd->client->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) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /SILCOPER <username> [-pubkey]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    /* Get passphrase */
+    cmd->client->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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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) {
+      cmd->client->ops->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) {
+      cmd->client->ops->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) {
+    cmd->client->ops->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_send_command(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;
+  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->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /LEAVE <channel>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      cmd->client->ops->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 ID of the channel */
+  if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  channel = (SilcChannelEntry)id_cache->context;
+  channel->on_channel = FALSE;
+
+  /* Send LEAVE command to the server */
+  idp = silc_id_payload_encode(id_cache->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) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /USERS <channel>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      cmd->client->ops->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;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                    "Usage: /GETKEY <nickname or server name>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->pending) {
+    SilcClientCommandReplyContext reply = 
+      (SilcClientCommandReplyContext)context2;
+    SilcCommandStatus status;
+    unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
+    SILC_GET16_MSB(status, tmp);
+    
+    if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+       status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                           "%s", 
+                           silc_client_command_status_message(status));
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  /* Parse the typed nickname. */
+  if (client->params->nickname_parse)
+    client->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) {
+      if (cmd->pending) {
+       COMMAND_ERROR;
+       goto out;
+      }
+
+      /* No. what ever user wants we don't have it, so resolve it. We
+        will try to resolve both client and server, one of them is
+        bound to be wrong. */
+
+      /* This will send the IDENTIFY command */
+      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_destructor,
+                                 silc_client_command_getkey, 
+                                 silc_client_command_dup(cmd));
+
+      /* This sends the IDENTIFY command to resolve the server. */
+      silc_client_send_command(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, NULL,
+                                 silc_client_command_getkey, 
+                                 silc_client_command_dup(cmd));
+
+      cmd->pending = 1;
+      silc_free(nickname);
+      return;
+    }
+
+    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);
+}
similarity index 59%
rename from apps/silc/command.h
rename to lib/silcclient/command.h
index 19db809b936484e47199e6a98002ca3dbce81cfb..79b4213028c4fdc904313209f082405ef5d28d4b 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
        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.
+       flags on client side as well (actually we preserve them but
+       ignore them :)).
 
 */
 typedef struct {
@@ -53,41 +51,46 @@ typedef struct {
   SilcCommand cmd;
   char *name;
   SilcCommandFlag flags;
-  unsigned int max_args;
+  uint32 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 */
+/* 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. */
 typedef struct {
   SilcClient client;
-  SilcSocketConnection sock;
-  unsigned int argc;
+  SilcClientConnection conn;
+  SilcClientCommand *command;
+  uint32 argc;
   unsigned char **argv;
-  unsigned int *argv_lens;
-  unsigned int *argv_types;
+  uint32 *argv_lens;
+  uint32 *argv_types;
+  int pending;                 /* Command is being re-processed when TRUE */
+  int users;                   /* Reference counter */
 } *SilcClientCommandContext;
 
+#include "silcapi.h"
+
 /* 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;
+  SilcCommandCb callback;
+  SilcClientPendingDestructor destructor;
   void *context;
-  SilcClientCommandCallback callback;
-
+  uint16 ident;
   struct SilcClientCommandPendingStruct *next;
 } SilcClientCommandPending;
 
 /* List of pending commands */
 extern SilcClientCommandPending *silc_command_pending;
 
+#include "command_reply.h"
+
 /* Macros */
 
 /* Macro used for command declaration in command list structure */
@@ -96,45 +99,37 @@ extern SilcClientCommandPending *silc_command_pending;
 
 /* 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;                                          \
-      }                                                        \
-    }                                                  \
-  }                                                    \
+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);       \
 } 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));            \
-  }                                                    \
+/* Execute destructor for pending command */
+#define SILC_CLIENT_PENDING_DESTRUCTOR(ctx, cmd)                       \
+do {                                                                   \
+  silc_client_command_pending_del((ctx)->sock->user_data, (cmd),       \
+                                 (ctx)->ident);                        \
+  if (ctx->destructor)                                                 \
+    (*ctx->destructor)(ctx->context);                                  \
 } while(0)
 
-/* Prototypes */
-void silc_client_command_pending(SilcCommand reply_cmd,
-                                SilcClientCommandCallback callback,
-                                void *context);
-void silc_client_command_pending_del(SilcCommand reply_cmd);
+/* Prototypes (some prototypes are in the silcapi.h file) */
+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(server);
 SILC_CLIENT_CMD_FUNC(list);
 SILC_CLIENT_CMD_FUNC(topic);
 SILC_CLIENT_CMD_FUNC(invite);
@@ -148,17 +143,14 @@ 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(restart);
+SILC_CLIENT_CMD_FUNC(ban);
 SILC_CLIENT_CMD_FUNC(close);
-SILC_CLIENT_CMD_FUNC(die);
+SILC_CLIENT_CMD_FUNC(shutdown);
 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);
+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..a6ce15b
--- /dev/null
@@ -0,0 +1,1905 @@
+/*
+
+  command_reply.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.
+
+*/
+/*
+ * 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.
+ */
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.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(kill, KILL),
+  SILC_CLIENT_CMD_REPLY(info, INFO),
+  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(cumode, CUMODE),
+  SILC_CLIENT_CMD_REPLY(kick, KICK),
+  SILC_CLIENT_CMD_REPLY(ban, BAN),
+  SILC_CLIENT_CMD_REPLY(close, CLOSE),
+  SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
+  SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
+  SILC_CLIENT_CMD_REPLY(leave, LEAVE),
+  SILC_CLIENT_CMD_REPLY(users, USERS),
+  SILC_CLIENT_CMD_REPLY(getkey, GETKEY),
+
+  { NULL, 0 },
+};
+
+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),    "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->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->ops->command_reply(cmd->client, \
+  cmd->sock->user_data, cmd->payload, FALSE, \
+  silc_command_get(cmd->payload), status)
+
+/* 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;
+  SilcClientCommandReply *cmd;
+  SilcClientCommandReplyContext ctx;
+  SilcCommandPayload payload;
+  SilcCommand command;
+  uint16 ident;
+
+  /* Get command reply payload from packet */
+  payload = silc_command_payload_parse(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;
+  ctx->args = silc_command_get_args(ctx->payload);
+  ctx->packet = packet;
+  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), ident);
+
+  /* Execute command reply */
+  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_free(ctx);
+    return;
+  }
+
+  cmd->cb(ctx, NULL);
+}
+
+/* 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)
+{
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientID *client_id;
+  SilcIDCacheEntry id_cache = NULL;
+  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) {
+    COMMAND_REPLY_ERROR;
+    return;
+  }
+  
+  client_id = silc_id_payload_parse_id(id_data, len);
+  if (!client_id) {
+    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) {
+    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. */
+  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache)) {
+    SILC_LOG_DEBUG(("Adding new client entry"));
+    client_entry = 
+      silc_client_add_client(cmd->client, conn, nickname, username, realname,
+                            client_id, mode);
+  } else {
+    client_entry = (SilcClientEntry)id_cache->context;
+    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)
+    COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
+                  channels, mode, idle));
+
+  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;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  /* Save WHOIS info */
+  silc_client_command_reply_whois_save(cmd, status);
+
+  /* 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);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
+  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;
+  SilcIDCacheEntry id_cache = NULL;
+  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 */
+  if (silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                     NULL, NULL, 
+                                     silc_hash_client_id_compare, NULL,
+                                     &id_cache)) 
+    client_entry = (SilcClientEntry)id_cache->context;
+  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_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
+  silc_client_command_reply_free(cmd);
+}
+
+static void 
+silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
+                                       SilcCommandStatus status)
+{
+  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) {
+    COMMAND_REPLY_ERROR;
+    return;
+  }
+  idp = silc_id_payload_parse_data(id_data, len);
+  if (!idp) {
+    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. */
+    if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
+                                        (void *)client_id, 
+                                        NULL, NULL, 
+                                        silc_hash_client_id_compare, NULL,
+                                        &id_cache)) {
+      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 {
+      client_entry = (SilcClientEntry)id_cache->context;
+      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 */
+    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, FALSE);
+    } else {
+      server_entry = (SilcServerEntry)id_cache->context;
+    }
+
+    /* Notify application */
+    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. */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, 
+                                    (void *)channel_id, &id_cache)) {
+      if (!name)
+       break;
+
+      SILC_LOG_DEBUG(("Adding new channel entry"));
+      channel_entry = silc_client_new_channel_id(client, conn->sock, 
+                                                strdup(name), 0, idp);
+    } else {
+      channel_entry = (SilcChannelEntry)id_cache->context;
+    }
+
+    /* Notify application */
+    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;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  /* Save IDENTIFY info */
+  silc_client_command_reply_identify_save(cmd, status);
+
+  /* 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);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
+  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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  silc_client_receive_new_id(cmd->client, cmd->sock, idp);
+    
+  /* Notify application */
+  COMMAND_REPLY((ARGS, conn->local_entry));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
+  SILC_CLIENT_PENDING_DESTRUCTOR(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_PENDING_DESTRUCTOR(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;
+  SilcIDCacheEntry id_cache = 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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, topic));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
+  SILC_CLIENT_PENDING_DESTRUCTOR(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;
+  SilcIDCacheEntry id_cache;
+  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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* 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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->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, FALSE);
+  } 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_CLIENT_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  for (i = 0; i < conn->ping_count; i++) {
+    if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
+      diff = curtime - conn->ping[i].start_time;
+      cmd->client->ops->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_PENDING_DESTRUCTOR(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;
+  SilcIDPayload idp = NULL;
+  SilcChannelEntry channel;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelUser chu;
+  uint32 argc, mode, 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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->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) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                         "Cannot join channel: Bad reply packet");
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  channel_name = strdup(tmp);
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (!tmp) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                         "Cannot join channel: Bad reply packet");
+    COMMAND_REPLY_ERROR;
+    silc_free(channel_name);
+    goto out;
+  }
+  idp = silc_id_payload_parse_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    silc_free(channel_name);
+    goto out;
+  }
+
+  /* Get channel mode */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (tmp)
+    SILC_GET32_MSB(mode, tmp);
+  else
+    mode = 0;
+
+  /* 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);
+
+  /* If we have the channel entry, remove it and create a new one */
+  channel = silc_client_get_channel(cmd->client, conn, channel_name);
+  if (channel)
+    silc_client_del_channel(cmd->client, conn, channel);
+
+  /* Save received Channel ID. This actually creates the channel */
+  channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
+                                      mode, idp);
+  silc_id_payload_free(idp);
+
+  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)) {
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                           "Cannot join channel: Unsupported HMAC `%s'",
+                           hmac);
+      COMMAND_REPLY_ERROR;
+      silc_free(channel_name);
+      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. */
+    if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
+                                        (void *)client_id, 
+                                        NULL, NULL, 
+                                        silc_hash_client_id_compare, NULL,
+                                        &id_cache)) {
+      /* 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);
+    } else {
+      /* Yes, we have it already */
+      client_entry = (SilcClientEntry)id_cache->context;
+    }
+
+    /* Join the client to the channel */
+    chu = silc_calloc(1, sizeof(*chu));
+    chu->client = client_entry;
+    chu->mode = mode;
+    silc_list_add(channel->clients, 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 (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+    silc_client_save_channel_key(conn, keyp, channel);
+
+  /* Client is now joined to the channel */
+  channel->on_channel = TRUE;
+
+  /* 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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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] = ' ';
+       
+       cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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;
+  SilcIDCacheEntry id_cache;
+  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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* 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_PENDING_DESTRUCTOR(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;
+  SilcIDCacheEntry id_cache = NULL;
+  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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* 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 */
+  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache)) {
+    silc_free(channel_id);
+    silc_free(client_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  client_entry = (SilcClientEntry)id_cache->context;
+
+  /* Save the mode */
+  SILC_GET32_MSB(mode, modev);
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (chu->client == client_entry) {
+      chu->mode = mode;
+      break;
+    }
+  }
+
+  /* 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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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;
+  SilcIDCacheEntry id_cache = NULL;
+  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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* 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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
+  silc_client_command_reply_free(cmd);
+}
+
+/* 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;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+  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) {
+    cmd->client->ops->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 */
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                  &id_cache)) {
+    /* Resolve the channel from server */
+    silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
+    
+    /* 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_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                               NULL, silc_client_command_reply_users, cmd);
+    return;
+  } else {
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  /* Remove old client list from channel. */
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    silc_list_del(channel->clients, chu);
+    silc_free(chu);
+  }
+
+  /* Cache the received Client ID's and modes. */
+  for (i = 0; i < list_count; i++) {
+    uint16 idp_len;
+    uint32 mode;
+    SilcClientID *client_id;
+    SilcClientEntry client;
+
+    /* 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. */
+    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 (!id_cache || !((SilcClientEntry)id_cache->context)->username ||
+       !((SilcClientEntry)id_cache->context)->realname) {
+
+      if (id_cache && id_cache->context) {
+       SilcClientEntry client_entry = (SilcClientEntry)id_cache->context;
+       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+         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 {
+      /* Found the client, join it to the channel */
+      client = (SilcClientEntry)id_cache->context;
+      chu = silc_calloc(1, sizeof(*chu));
+      chu->client = client;
+      chu->mode = mode;
+      silc_list_add(channel->clients, chu);
+
+      silc_free(client_id);
+      id_cache = NULL;
+    }
+
+    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 */
+    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,
+                               NULL, silc_client_command_reply_users, cmd);
+
+    silc_buffer_free(res_cmd);
+    if (channel_id)
+      silc_free(channel_id);
+
+    silc_free(res_argv);
+    silc_free(res_argv_lens);
+    silc_free(res_argv_types);
+    return;
+  }
+
+  /* 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_PENDING_DESTRUCTOR(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) {
+    cmd->client->ops->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_data(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) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+    
+    if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+  } 
+   
+  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);
+    if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
+                                        (void *)client_id, 
+                                        NULL, NULL, 
+                                        silc_hash_client_id_compare, NULL,
+                                        &id_cache)) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+
+    client_entry = (SilcClientEntry)id_cache->context;
+
+    /* 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);
+  SILC_CLIENT_PENDING_DESTRUCTOR(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);
+}
similarity index 73%
rename from apps/silc/command_reply.h
rename to lib/silcclient/command_reply.h
index 36b312860733035d2df58f02f963c30bc38c93ad..1c50430fed8f6b4f838e9ac97f96744d9ca81ecd 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
@@ -35,10 +35,14 @@ typedef struct {
   SilcClient client;
   SilcSocketConnection sock;
   SilcCommandPayload payload;
+  SilcArgumentPayload args;
+  SilcPacketContext *packet;
 
   /* If defined this executes the pending command. */
+  SilcClientPendingDestructor destructor;
+  SilcCommandCb callback;
   void *context;
-  SilcClientCommandCallback callback;
+  uint16 ident;
 } *SilcClientCommandReplyContext;
 
 /* Macros */
@@ -49,29 +53,23 @@ typedef struct {
 
 /* 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)
+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
+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 +77,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 +90,14 @@ 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);
 
 #endif
diff --git a/lib/silcclient/idlist.c b/lib/silcclient/idlist.c
new file mode 100644 (file)
index 0000000..3444819
--- /dev/null
@@ -0,0 +1,1022 @@
+/*
+
+  idlist.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 "clientlibincludes.h"
+#include "client_internal.h"
+
+typedef struct {
+  SilcClientCommandContext cmd;
+  SilcGetClientCallback completion;
+  char *nickname;
+  char *server;
+  void *context;
+  int found;
+} *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->cmd->client, i->cmd->conn,
+                                         i->nickname, i->server,
+                                         &clients_count);
+  if (clients) {
+    i->completion(i->cmd->client, i->cmd->conn, clients, 
+                 clients_count, i->context);
+    i->found = TRUE;
+    silc_free(clients);
+  }
+}
+
+static void silc_client_get_client_destructor(void *context)
+{
+  GetClientInternal i = (GetClientInternal)context;
+
+  if (i->found == FALSE)
+    i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context);
+
+  silc_client_command_free(i->cmd);
+  if (i->nickname)
+    silc_free(i->nickname);
+  if (i->server)
+    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)
+{
+  char ident[512];
+  SilcClientCommandContext ctx;
+  GetClientInternal i = silc_calloc(1, sizeof(*i));
+      
+  /* No ID found. Do query from the server. The query is done by 
+     sending simple IDENTIFY command to the server. */
+  ctx = silc_client_command_alloc();
+  ctx->client = client;
+  ctx->conn = conn;
+  ctx->command = silc_client_command_find("IDENTIFY");
+  memset(ident, 0, sizeof(ident));
+  snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
+  silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
+                         &ctx->argv_types, &ctx->argc, 2);
+
+  i->cmd = silc_client_command_dup(ctx);
+  i->nickname = nickname ? strdup(nickname) : NULL;
+  i->server = server ? strdup(server) : NULL;
+  i->completion = completion;
+  i->context = context;
+
+  /* Call the command */
+  ctx->command->cb(ctx, NULL);
+
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                             conn->cmd_ident, 
+                             silc_client_get_client_destructor,
+                             silc_client_command_get_client_callback, 
+                             (void *)i);
+}
+
+/* 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;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  uint32 list_count;
+  SilcBuffer client_id_list;
+  SilcGetClientCallback completion;
+  void *context;
+  int found;
+} *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;
+  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++;
+      i->found = TRUE;
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+  }
+
+  if (i->found) {
+    i->completion(i->client, i->conn, clients, clients_count, i->context);
+    silc_free(clients);
+  }
+}
+
+static void silc_client_get_clients_list_destructor(void *context)
+{
+  GetClientsByListInternal i = (GetClientsByListInternal)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (i->found == FALSE)
+    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);
+
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                               conn->cmd_ident, 
+                               silc_client_get_clients_list_destructor,
+                               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);
+}
+
+/* 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. */
+
+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) {
+      char ident[512];
+      SilcClientCommandContext ctx;
+      
+      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_client_command_alloc();
+      ctx->client = client;
+      ctx->conn = conn;
+      ctx->command = silc_client_command_find("IDENTIFY");
+      memset(ident, 0, sizeof(ident));
+      snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
+      silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
+                             &ctx->argv_types, &ctx->argc, 2);
+      ctx->command->cb(ctx, NULL);
+      
+      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;
+}
+
+/* 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;
+  int found;
+} *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);
+    i->found = TRUE;
+  }
+}
+
+static void silc_client_get_client_by_id_destructor(void *context)
+{
+  GetClientByIDInternal i = (GetClientByIDInternal)context;
+
+  if (i->found == FALSE)
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  if (i->client_id)
+    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"));
+
+  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
+                          ++conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  i->client = client;
+  i->conn = conn;
+  i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
+                             conn->cmd_ident, 
+                             silc_client_get_client_by_id_destructor,
+                             silc_client_command_get_client_by_id_callback, 
+                             (void *)i);
+}
+
+/* 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);
+
+  /* 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, FALSE)) {
+    silc_free(client_entry->nickname);
+    silc_free(client_entry->username);
+    silc_free(client_entry->hostname);
+    silc_free(client_entry->server);
+    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, FALSE);
+  }
+}
+
+/* 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->server);
+  silc_free(client_entry->id);
+  silc_free(client_entry->fingerprint);
+  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);
+  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);
+  silc_client_del_client_entry(client, conn, client_entry);
+  return ret;
+}
+
+/* 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);
+  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;
+}
+
+/* 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;
+
+  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;
+
+  return entry;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcChannelID *channel_id;
+  SilcGetChannelCallback completion;
+  void *context;
+  int found;
+} *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);
+    i->found = TRUE;
+  }
+}
+
+static void silc_client_get_channel_by_id_destructor(void *context)
+{
+  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
+
+  if (i->found == FALSE)
+    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"));
+
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                          ++conn->cmd_ident,
+                          1, 5, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  i->client = client;
+  i->conn = conn;
+  i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                             conn->cmd_ident, 
+                             silc_client_get_channel_by_id_destructor,
+                             silc_client_command_get_channel_by_id_callback, 
+                             (void *)i);
+}
+
+/* Find channel entry by ID. This routine is used internally by the library. */
+
+SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id,
+                                              int query)
+{
+  SilcBuffer idp;
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  if (channel)
+    return channel;
+
+  if (query) {
+    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+    silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                            ++conn->cmd_ident,
+                            1, 5, idp->data, idp->len);
+    silc_buffer_free(idp);
+  }
+
+  return NULL;
+}
+
+/* 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;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client->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->params->nickname_force_format)
+    return;
+
+  len = 0;
+  for (i = 0; i < clients_count; i++)
+    if (clients[i]->valid)
+      len++;
+  if (!len)
+    return;
+
+  cp = client->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)
+         break;
+
+       for (i = 0; i < clients_count; 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;
+
+  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..26b38e0
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+
+  idlist.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.
+
+*/
+
+#ifndef IDLIST_H
+#define IDLIST_H
+
+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 */
+} *SilcClientEntry;
+
+/* Client and its mode on a channel */
+typedef struct SilcChannelUserStruct {
+  SilcClientEntry client;
+  uint32 mode;
+  struct SilcChannelUserStruct *next;
+} *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 */
+typedef struct SilcChannelEntryStruct {
+  char *channel_name;
+  SilcChannelID *id;
+  uint32 mode;
+  bool on_channel;
+
+  /* Joined clients */
+  SilcList clients;
+
+  /* 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;
+} *SilcChannelEntry;
+
+/* 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_idlist_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id,
+                                              int query);
+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..9721497
--- /dev/null
@@ -0,0 +1,1170 @@
+/*
+
+  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->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->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;
+  if (min < min2)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  if (status != SILC_SKE_STATUS_OK)
+    client->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) {
+    if (ske->status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                      "Received unsupported server %s public key",
+                      ctx->sock->hostname);
+    } else if (ske->status == SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED) {
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                      "Remote host did not send its public key, even though "
+                      "it must send it");
+    } else {
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "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;
+  }
+
+  /* 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->silc_client_version,
+                                         ctx->packet->buffer, TRUE);
+      } else {
+       SilcSKEStartPayload *start_payload;
+
+       /* Assemble security properties. */
+       silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_NONE, 
+                                             client->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->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                          "Received unsupported server %s public key",
+                          ctx->sock->hostname);
+        } else {
+          client->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->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
+                        "Password authentication required by server %s",
+                        ctx->sock->hostname);
+       client->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_get_group_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_get_group_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..155ea2d
--- /dev/null
@@ -0,0 +1,1930 @@
+/*
+
+  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 */
+} 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. */
+  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_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
+ *
+ *    int 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.
+ *
+ ***/
+bool 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.
+ *
+ * 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. 
+ *
+ ***/
+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. 
+ *
+ ***/
+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. 
+ *
+ ***/
+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 (*SilcGetClientCallback)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcClientEntry *clients,
+ *                                          uint32 clients_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);
+
+/* Command management (command.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_command_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientCommandContext silc_client_command_alloc();
+ *
+ * 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();
+
+/****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(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. 
+ *
+ ***/
+SilcClientCommand *silc_client_command_find(const char *name);
+
+/****f* silcclient/SilcClientAPI/silc_client_send_command
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_send_command(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. 
+ *
+ ***/
+void silc_client_send_command(SilcClient client, SilcClientConnection conn,
+                             SilcCommand command, uint16 ident,
+                             uint32 argc, ...);
+
+/****f* silcclient/SilcClientAPI/SilcClientPendingDestructor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcClientPendingDestructor)(void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Pending Command callback destructor. This is called after calling the
+ *    pending callback or if error occurs while processing the pending command.
+ *    If error occurs then the callback won't be called at all, and only this
+ *    destructor is called. The `context' is the context given for the function
+ *    silc_client_command_pending. 
+ *
+ ***/
+typedef void (*SilcClientPendingDestructor)(void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_pending
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_pending(SilcClientConnection conn,
+ *                                     SilcCommand reply_cmd,
+ *                                     uint16 ident,
+ *                                     SilcClientPendingDestructor destructor,
+ *                                     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'. 
+ *
+ ***/
+void silc_client_command_pending(SilcClientConnection conn,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcClientPendingDestructor destructor,
+                                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. 
+ *
+ ***/
+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);
+
+typedef enum {
+  SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
+  SILC_CLIENT_FILE_MONITOR_SEND,
+  SILC_CLIENT_FILE_MONITOR_RECEIVE,
+  SILC_CLIENT_FILE_MONITOR_GET,
+  SILC_CLIENT_FILE_MONITOR_PUT,
+  SILC_CLIENT_FILE_MONITOR_CLOSED,
+  SILC_CLIENT_FILE_MONITOR_ERROR,
+} SilcClientMonitorStatus;
+
+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,
+} SilcClientFileError;
+
+/****f* silcclient/SilcClientAPI/silc_client_file_receive
+ *
+ * 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 `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
+ *
+ *    uint32 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);
+ *
+ * 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.  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. This returns 0 if the
+ *    file indicated by the `filepath' is being transmitted to the remote
+ *    client indicated by the `client_entry', already.
+ *
+ *    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.
+ *
+ ***/
+uint32 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);
+
+/****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..7da6935
--- /dev/null
@@ -0,0 +1,26 @@
+<!--
+@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=silcpayload.html:SILC Payload API
+@LINK=silcprivate.html:SILC Private API
+-->
+
+<FONT SIZE="+3">SILC Core Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+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.
+</FONT>
+</PRE>
+
+@LINKS@
index 5811b56a5438b3b3cc0cae1f9feb90f67149ca7a..04e898cd4da4800a965c9f327de913a1a6fb42e4 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
+       silcpayload.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    \
+       silcpayload.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/silcauth.c b/lib/silccore/silcauth.c
new file mode 100644 (file)
index 0000000..7ac372d
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+
+  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(unsigned char *data,
+                                       uint32 data_len)
+{
+  SilcBufferStruct buffer;
+  SilcAuthPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Authentication Payload"));
+
+  silc_buffer_set(&buffer, 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,
+                                   unsigned char *random_data,
+                                   uint16 random_len,
+                                   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, 
+                                unsigned char *random,
+                                uint32 random_len, 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,
+                                             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. */
+
+int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+                                    SilcPublicKey public_key, SilcHash hash,
+                                    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. */
+
+int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
+                                         SilcPublicKey public_key, 
+                                         SilcHash hash,
+                                         void *id, SilcIdType type)
+{
+  SilcAuthPayload auth_payload;
+  int ret;
+
+  auth_payload = silc_auth_payload_parse(payload->data, 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. */
+
+int silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
+                    void *auth_data, uint32 auth_data_len, 
+                    SilcHash hash, 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, payload->auth_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. */
+
+int silc_auth_verify_data(unsigned char *payload, uint32 payload_len,
+                         SilcAuthMethod auth_method, void *auth_data,
+                         uint32 auth_data_len, SilcHash hash, 
+                         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(SilcBuffer buffer)
+{
+  SilcKeyAgreementPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
+
+  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(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..5f15f8a
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+
+  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(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(unsigned char *data,
+                                       uint32 data_len);
+
+/****f* silccore/SilcAuthAPI/silc_auth_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
+ *                                        unsigned char *random_data,
+ *                                        uint16 random_len,
+ *                                        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,
+                                   unsigned char *random_data,
+                                   uint16 random_len,
+                                   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,
+ *                                                  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,
+                                             void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_public_key_auth_verify
+ *
+ * SYNOPSIS
+ *
+ *    int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+ *                                         SilcPublicKey public_key, 
+ *                                         SilcHash hash,
+ *                                         void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Verifies the authentication data. Returns TRUE if authentication was
+ *    successful.
+ *
+ ***/
+int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+                                    SilcPublicKey public_key, SilcHash hash,
+                                    void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_public_key_auth_verify_data
+ *
+ * SYNOPSIS
+ *
+ *    int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
+ *                                              SilcPublicKey public_key, 
+ *                                              SilcHash hash,
+ *                                              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.
+ *
+ ***/
+int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
+                                         SilcPublicKey public_key, 
+                                         SilcHash hash,
+                                         void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_verify
+ *
+ * SYNOPSIS
+ *
+ *    int silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
+ *                         void *auth_data, uint32 auth_data_len, 
+ *                         SilcHash hash, 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.
+ *
+ ***/
+int silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
+                    void *auth_data, uint32 auth_data_len, 
+                    SilcHash hash, void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_verify_data
+ *
+ * SYNOPSIS
+ *
+ *    int silc_auth_verify_data(unsigned char *payload, uint32 payload_len,
+ *                              SilcAuthMethod auth_method, void *auth_data,
+ *                              uint32 auth_data_len, SilcHash hash, 
+ *                              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.
+ *
+ ***/
+int silc_auth_verify_data(unsigned char *payload, uint32 payload_len,
+                         SilcAuthMethod auth_method, void *auth_data,
+                         uint32 auth_data_len, SilcHash hash, 
+                         void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcKeyAgreementPayload 
+ *    silc_key_agreement_payload_parse(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *    Parses and returns an allocated Key Agreement payload.
+ *
+ ***/
+SilcKeyAgreementPayload silc_key_agreement_payload_parse(SilcBuffer buffer);
+
+/****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(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..9ba67a7c56989b0c2da3581a903690842bfdfa67 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;
-};
-
-/* 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;
+  uint16 name_len;
+  unsigned char *channel_name;
+  uint16 id_len;
+  unsigned char *channel_id;
+  uint32 mode;
 };
 
-/* 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(SilcBuffer buffer)
 {
   SilcChannelPayload new;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing channel payload"));
 
   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 +71,345 @@ 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(SilcBuffer buffer)
+{
+  SilcDList list;
+  SilcChannelPayload new;
+  int len, ret;
+
+  SILC_LOG_DEBUG(("Parsing channel payload list"));
+
+  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(unsigned char *channel_name,
+                                      uint16 channel_name_len,
+                                      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. */
+
+int 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];
+
+  /* Decrypt the channel message. 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 successfull
+     and we have the channel message ready to be displayed. */
+  end = data + data_len;
+
+  /* Push the IV out of the packet */
+  iv_len = silc_cipher_get_block_len(cipher);
+
+  /* Decrypt the channel message */
+  silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len));
+
+  /* Take the MAC */
+  if (hmac) {
+    mac_len = silc_hmac_len(hmac);
+    mac = (end - iv_len - mac_len);
+
+    /* Check the MAC of the message */
+    SILC_LOG_DEBUG(("Checking channel message MACs"));
+    silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len);
+    if (memcmp(mac, mac2, mac_len)) {
+      SILC_LOG_DEBUG(("Channel message MACs does not match"));
+      return FALSE;
+    }
+    SILC_LOG_DEBUG(("MAC is Ok"));
+  }
+
+  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(SilcBuffer buffer,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac)
+{
+  SilcChannelMessagePayload new;
+  int ret;
+  uint32 iv_len, mac_len;
+
+  SILC_LOG_DEBUG(("Parsing channel message payload"));
+
+  /* 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 < 1 || 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,
+                                              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);
-
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
 
-  /* 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 +417,58 @@ 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_mesage_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(SilcBuffer buffer)
 {
   SilcChannelKeyPayload new;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing channel key payload"));
 
   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 +491,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,
+SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
                                           unsigned char *id,
-                                          unsigned short cipher_len,
+                                          uint16 cipher_len,
                                           unsigned char *cipher,
-                                          unsigned short key_len,
+                                          uint16 key_len,
                                           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 +523,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 +541,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 +552,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 +563,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..fdd8e663850c5bb3838655e841cac4d9587d2bd5 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,
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *    Parses channel payload returning new channel payload structure. The
+ *    `buffer' is the raw payload buffer.
+ *
+ ***/
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_parse_list
+ *
+ * SYNOPSIS
+ *
+ *    SilcDList silc_channel_payload_parse_list(SilcBuffer buffer);
+ *
+ * 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(SilcBuffer buffer);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_payload_encode(unsigned char *channel_name,
+ *                                           uint16 channel_name_len,
+ *                                           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(unsigned char *channel_name,
+                                      uint16 channel_name_len,
+                                      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
+ *
+ *    int 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.
+ *
+ ***/
+int 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(SilcBuffer buffer,
+ *                                       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(SilcBuffer buffer,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_message_payload_encode(uint16 flags,
+ *                                                   uint16 data_len,
+ *                                                   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,
+                                              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(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *     Parses channel key payload returning new channel key payload 
+ *     structure.
+ *
+ ***/
+SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
+ *                                               unsigned char *id,
+ *                                               uint16 cipher_len,
+ *                                               unsigned char *cipher,
+ *                                               uint16 key_len,
+ *                                               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,
                                           unsigned char *id,
-                                          unsigned short cipher_len,
+                                          uint16 cipher_len,
                                           unsigned char *cipher,
-                                          unsigned short key_len,
+                                          uint16 key_len,
                                           unsigned char *key);
-void silc_channel_key_free_payload(SilcChannelKeyPayload payload);
+
+/****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..e76ad180f69ee55fa415ef82d8f67edd9de47cd8 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(SilcBuffer 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 payload_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing command payload"));
 
   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(&payload_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) {
     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, 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_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_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);
+
+  return buffer;
+}
+
+/* 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;
+  uint32 *argv_lens = NULL, *argv_types = NULL;
+  unsigned char *x;
+  uint32 x_len;
+  uint32 x_type;
+  SilcBuffer buffer;
+  int i, k;
 
   argv = silc_calloc(argc, sizeof(unsigned char *));
-  argv_lens = silc_calloc(argc, sizeof(unsigned int));
-  argv_types = silc_calloc(argc, sizeof(unsigned int));
+  argv_lens = silc_calloc(argc, sizeof(uint32));
+  argv_types = silc_calloc(argc, sizeof(uint32));
 
-  for (i = 0; i < argc; i++) {
+  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, unsigned int);
+    x_len = va_arg(ap, uint32);
+
+    if (!x_type || !x || !x_len)
+      continue;
 
-    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;
+    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 +243,112 @@ 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;
+  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;
 
-  if (payload) {
-    for (i = 0; i < payload->argc; i++)
-      silc_free(payload->argv[i]);
+  va_start(ap, argc);
 
-    silc_free(payload->argv);
-    silc_free(payload);
-  }
-}
+  argc++;
+  argv = silc_calloc(argc, sizeof(unsigned char *));
+  argv_lens = silc_calloc(argc, sizeof(uint32));
+  argv_types = silc_calloc(argc, sizeof(uint32));
 
-/* Returns the command type in payload */
+  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;
 
-SilcCommand silc_command_get(SilcCommandPayload payload)
-{
-  return payload->cmd;
-}
+  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);
 
-/* Returns number of arguments in payload */
+    if (!x_type || !x || !x_len)
+      continue;
 
-unsigned int silc_command_get_arg_num(SilcCommandPayload payload)
-{
-  return payload->argc;
-}
+    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++;
+  }
 
-/* Returns first argument from payload. */
+  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
+                                      argv_types, ident);
 
-unsigned char *silc_command_get_first_arg(SilcCommandPayload payload,
-                                         unsigned int *ret_len)
-{
-  payload->pos = 0;
+  for (i = 0; i < k; i++)
+    silc_free(argv[i]);
+  silc_free(argv);
+  silc_free(argv_lens);
+  silc_free(argv_types);
 
-  if (ret_len)
-    *ret_len = payload->argv_lens[payload->pos];
+  va_end(ap);
 
-  return payload->argv[payload->pos++];
+  return buffer;
 }
 
-/* Returns next argument from payload or NULL if no more arguments. */
+/* Frees Command Payload */
 
-unsigned char *silc_command_get_next_arg(SilcCommandPayload payload,
-                                        unsigned int *ret_len)
+void silc_command_payload_free(SilcCommandPayload payload)
 {
-  if (payload->pos >= payload->argc)
-    return NULL;
-
-  if (ret_len)
-    *ret_len = payload->argv_lens[payload->pos];
-
-  return payload->argv[payload->pos++];
+  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..e1051e141d81d0321c22ad3ba6fef55f8c103127 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);
+/****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);
 
-/* Typedefinition for SILC commands. */
-typedef unsigned char SilcCommand;
-
-/* 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,241 @@ 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(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *    Parses command payload returning new command payload structure. The
+ *    `buffer' is the raw payload.
+ *
+ ***/
+SilcCommandPayload silc_command_payload_parse(SilcBuffer buffer);
+
+/****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_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/silcid.c b/lib/silccore/silcid.c
new file mode 100644 (file)
index 0000000..f9ca89d
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+
+  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
+
+/* 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;
+  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(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(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(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..2397888
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+  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.
+ *
+ ***/
+
+#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/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(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(void *id, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_str2id
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_str2id(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(unsigned char *id, uint32 id_len, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_get_len
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_id_get_len(void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the true length of the ID of the type `type'.
+ *
+ ***/
+uint32 silc_id_get_len(void *id, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_dup
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_dup(void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the ID of the type `type'. The caller must free the
+ *    duplicated ID.
+ *
+ ***/
+void *silc_id_dup(void *id, SilcIdType type);
+
+#endif
diff --git a/lib/silccore/silcidcache.c b/lib/silccore/silcidcache.c
new file mode 100644 (file)
index 0000000..245e4df
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+
+  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;
+};
+
+/* 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 c;
+  uint32 curtime = time(NULL);
+
+  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 ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
+  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);
+  }
+
+  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);
+
+  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);
+
+  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); 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 += 5;
+    list->cache_dyn = silc_realloc(list->cache_dyn, 
+                                  sizeof(*list->cache) * (i));
+
+    /* NULL the reallocated area */
+    for (k = list->cache_dyn_count; k < i; k++)
+      list->cache_dyn[k] = NULL;
+
+    list->cache_dyn[list->cache_dyn_count] = cache;
+    list->cache_dyn_count = i;
+    list->cache_count++;
+  }
+}
+
+/* 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)
+{
+  int dyn = FALSE;
+  list->pos++;
+
+  if (list->pos >= sizeof(list->cache)) {
+    list->pos = 0;
+    dyn = TRUE;
+  }
+
+  if (dyn && list->pos >= list->cache_dyn_count)
+    return FALSE;
+
+  if (!dyn && !list->cache[list->pos])
+    return FALSE;
+  
+  if (dyn && !list->cache_dyn[list->pos])
+    return FALSE;
+  
+  if (ret) {
+    if (!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..cadda8c
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+  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 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);
+
+/****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/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.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..bee44ce
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+
+  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(SilcBuffer buffer)
+{
+  SilcNotifyPayload new;
+  uint16 len;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Notify payload"));
+
+  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, 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..d173a51
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+  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(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *    Parse notify payload buffer and return data into payload structure.
+ *    The `buffer' is the raw payload data.
+ *
+ ***/
+SilcNotifyPayload silc_notify_payload_parse(SilcBuffer buffer);
+
+/****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..0791a70f590832ed7828b130dfac1457ff035e57 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"));
     }
+    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. */
+
+void 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;
+  int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+  bool cont = TRUE;
+
+  /* Do not process for disconnected connection */
+  if (SILC_IS_DISCONNECTED(sock))
+    return;
+  
+  if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
+    return;
+
+  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;
+    }
+
+    /* 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;
+    }
 
-  /* 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;
+    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;
     }
 
-    silc_buffer_put_tail(dest, buf, len);
-    silc_buffer_pull_tail(dest, len);
+    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->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, block_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;
+    }
+
+    /* 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;
+
+  SILC_LOG_DEBUG(("Clearing inbound buffer"));
+  silc_buffer_clear(sock->inbuf);
+}
+
+/* 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(mac, buffer->tail, 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;
+      }
+    }
 
-  /* 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);
+    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
 
+    /* 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). */
 
-void silc_packet_decrypt(SilcCipher cipher, SilcBuffer buffer, 
-                        unsigned int len)
+static int silc_packet_decrypt_rest_special(SilcCipher cipher,
+                                           SilcHmac hmac,
+                                           SilcBuffer buffer)
 {
-  SILC_LOG_DEBUG(("Decrypting packet, cipher %s, len %d (%d)", 
-                 cipher->cipher->name, len, len - 2));
+  /* 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;
+}
+
+/* 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)
+{
+  /* 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;
+
+    /* 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;
 
-  /* 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 1;
+  }
 }
 
 /* Parses the packet. This is called when a whole packet is ready to be
@@ -167,10 +605,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 +624,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 +672,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 +692,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 +738,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:
-
-   --------------------------------------------
-   | 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
+/* Allocate packet context */
 
-   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..54e5453c0a6805670c397896e4687a45f40c09e2 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
+ *    uint16 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;
+ *    uint16 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
+ *    uint16 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
+ *
+ *    void 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.
+ *
+ ***/
+void 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/silcpayload.c b/lib/silccore/silcpayload.c
new file mode 100644 (file)
index 0000000..294ebac
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+
+  silcpayload.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.
+
+*/
+/* Implementation of generic payloads described in the protocol 
+   specification drafts. */
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcpayload.h"
+
+/******************************************************************************
+
+                                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(SilcBuffer buffer)
+{
+  SilcIDPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing ID payload"));
+
+  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;
+}
+
+/* Parses data and return ID payload into payload structure. */
+
+SilcIDPayload silc_id_payload_parse_data(unsigned char *data, 
+                                        uint32 len)
+{
+  SilcIDPayload new;
+  SilcBufferStruct buffer;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing ID payload"));
+
+  silc_buffer_set(&buffer, data, 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;
+
+  return new;
+
+ err:
+  silc_free(new);
+  return NULL;
+}
+
+/* Return the ID directly from the raw payload data. */
+
+void *silc_id_payload_parse_id(unsigned char *data, uint32 len)
+{
+  SilcBufferStruct buffer;
+  SilcIdType type;
+  uint16 idlen;
+  unsigned char *id_data = NULL;
+  int ret;
+  void *id;
+
+  silc_buffer_set(&buffer, 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(void *id, SilcIdType type)
+{
+  SilcBuffer buffer;
+  unsigned char *id_data;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Encoding %s ID payload",
+                 type == SILC_ID_CLIENT ? "Client" :
+                 type == SILC_ID_SERVER ? "Server" : "Channel"));
+
+  id_data = silc_id_id2str(id, type);
+  len = silc_id_get_len(id, type);
+
+  buffer = silc_buffer_alloc(4 + 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_XNSTRING(id_data, len),
+                    SILC_STR_END);
+  silc_free(id_data);
+
+  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;
+}
+
+/******************************************************************************
+
+                             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(SilcBuffer buffer,
+                                               uint32 argc)
+{
+  SilcArgumentPayload new;
+  uint16 payload_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"));
+
+  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(&payload_len),
+                              SILC_STR_UI_CHAR(&arg_type),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+    
+    new->argv_lens[i] = payload_len;
+    new->argv_types[i] = arg_type;
+
+    if (payload_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], 
+                                                         payload_len),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+
+    silc_buffer_pull(buffer, payload_len);
+    pull_len += 3 + payload_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/silcpayload.h b/lib/silccore/silcpayload.h
new file mode 100644 (file)
index 0000000..99c2439
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+
+  silcpayload.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/SilcGenericPayloadAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the generic payloads described in the protocol
+ * specification; ID Payload and Argument Payload. The ID Payload is
+ * used to represent an ID. The Argument Payload is used to include
+ * arguments to other payloads that needs arguments.
+ *
+ ***/
+
+#ifndef SILCPAYLOAD_H
+#define SILCPAYLOAD_H
+
+/****s* silccore/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/silc_id_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcIDPayload silc_id_payload_parse(SilcBuffer buffer);
+ *
+ * DESCRIPTION
+ *
+ *    Parses buffer and return ID payload into payload structure. The
+ *    `buffer' is raw payload buffer.
+ *
+ ***/
+SilcIDPayload silc_id_payload_parse(SilcBuffer buffer);
+
+/****f* silccore/SilcGenericPayloadAPI/silc_id_payload_parse_data
+ *
+ * SYNOPSIS
+ *
+ *    SilcIDPayload silc_id_payload_parse_data(unsigned char *data, 
+ *                                             uint32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses buffer and return ID payload into payload structure. The
+ *    `data' and `len' are the raw payload buffer. This is equivalent
+ *    to the silc_id_payload_parse function.
+ *
+ ***/
+SilcIDPayload silc_id_payload_parse_data(unsigned char *data, 
+                                        uint32 len);
+
+/****f* silccore/SilcGenericPayloadAPI/silc_id_payload_parse_id
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_payload_parse_id(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(unsigned char *data, uint32 len);
+
+/****f* silccore/SilcGenericPayloadAPI/silc_id_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_id_payload_encode(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(void *id, SilcIdType type);
+
+/****f* silccore/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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);
+
+/****f* silccore/SilcGenericPayloadAPI/silc_argument_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcArgumentPayload silc_argument_payload_parse(SilcBuffer buffer,
+ *                                                    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(SilcBuffer buffer,
+                                               uint32 argc);
+
+/****f* silccore/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/SilcGenericPayloadAPI/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/silcprivate.c b/lib/silccore/silcprivate.c
new file mode 100644 (file)
index 0000000..db83587
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+
+  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(SilcBuffer buffer, SilcCipher cipher)
+{
+  SilcPrivateMessagePayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing private message payload"));
+
+  /* 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_ERROR(("Incorrect private message payload"));
+    goto err;
+  }
+
+  if ((new->message_len < 1 || new->message_len > buffer->len)) {
+    SILC_LOG_ERROR(("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,
+                                              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..80a2c68
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+  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(SilcBuffer buffer, 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(SilcBuffer buffer, SilcCipher cipher);
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_private_message_payload_encode(uint16 flags,
+ *                                                   uint16 data_len,
+ *                                                   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,
+                                              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..25b8233
--- /dev/null
@@ -0,0 +1,22 @@
+<!--
+@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
+-->
+
+<FONT SIZE="+3">SILC Crypto Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+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.
+</FONT>
+</PRE>
+
+@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/silcmath/silcprimegen.h
rename to lib/silccrypt/aes.h
index d34ba67fa19fb6b617cbf403ff280bfeac7f6a31..6efd6582de3b6b87a82f2254d809e45ac851bfde 100644 (file)
@@ -1,7 +1,7 @@
 /*
 
-  silcprimegen.h
-  
+  rijndael.h
+
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
   Copyright (C) 1997 - 2000 Pekka Riikonen
 
 */
 
-#ifndef SILCPRIMEGEN_H
-#define SILCPRIMEGEN_H
+#ifndef RIJNDAEL_H
+#define RIJNDAEL_H
+
+/* 
+ * SILC Crypto API for Rijndael
+ */
 
-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();
+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..3b99b69
--- /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@poseidon.pspt.fi.
+
+   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..66aea06a875c825b232e6f1af6c0bec6f99af2ba 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,35 @@ 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;
 
   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;
 
-  return TRUE;
+  return key->bits;
 }
 
 /* Set private key. This derives the public key from the private
@@ -245,40 +255,44 @@ 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;
 
   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);
+    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);
+    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);
+    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;
 
   return TRUE;
 }
@@ -288,49 +302,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 +320,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 +342,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 +356,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 +378,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 +395,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 +414,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 +445,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,12 +456,12 @@ 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);
@@ -492,14 +471,18 @@ void rsa_generate_keys(RsaKey *key, unsigned int bits,
   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 modulus length */
+  key->bits = bits;
+
   /* Set the primes */
   silc_mp_set(&key->p, p);
   silc_mp_set(&key->q, q);
-  
+
   /* Compute modulus, n = p * q */
   silc_mp_mul(&key->n, &key->p, &key->q);
   
@@ -521,16 +504,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 +522,11 @@ 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);
+  silc_mp_uninit(&key->p);
+  silc_mp_uninit(&key->q);
+  silc_mp_uninit(&key->n);
+  silc_mp_uninit(&key->e);
+  silc_mp_uninit(&key->d);
 }
 
 /* RSA encrypt/decrypt function. cm = ciphertext or plaintext,
@@ -553,8 +537,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..33ffd88a6cf19bee3dfb5cf1324423d38acfde10 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 */
+  SilcMPInt p;                 /* prime p */
+  SilcMPInt q;                 /* prime q */
+  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..785cec1f5e07a99e26bc968871c4bae89269d456 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,100 @@ 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;
-    }
-    c = c->next;
-  }
+  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);
 
-      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 +197,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..7f951ee8dcc1cccdf732d4d8c2c6fb6092ed468a 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 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..68d3676
--- /dev/null
@@ -0,0 +1,176 @@
+/****h* silccrypt/SilcDH/silcdh.h
+ *
+ * NAME
+ *
+ * silcdh.h
+ *
+ * COPYRIGHT
+ *
+ * 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.
+ *
+ * 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"
+#include "silcrng.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) */
+
+  SilcRng rng;           /* RNG */
+};
+
+/****f* silccrypt/SilcDH/silc_dh_alloc
+ *
+ * SYNOPSIS
+ *    
+ *    SilcDH silc_dh_alloc(SilcRng rng, SilcMPInt *g, SilcMPInt *p, SilcMPInt *lpf);
+ * 
+ * DESCRIPTION
+ *
+ *    Allocate SilcDH context. The `rng' must be initialized random number 
+ *    generator 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(SilcRng rng, SilcMPInt *g, SilcMPInt *p, SilcMPInt *lpf);
+
+/****f* silccrypt/SilcDH/silc_dh_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_dh_free(SilcDH dh);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the SilcDH context. Does not free the RNG context given in the 
+ *    allocation. Frees all the allocated data inside the SilcDH context. 
+ *
+ ***/
+void silc_dh_free(SilcDH dh);
+
+/****f* silccrypt/SilcDH/silc_dh_generate_private
+ *
+ * SYNOPSIS
+ *
+ *    int silc_dh_generate_private(SilcDH dh, 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 no be freed by the caller. 
+ *
+ ***/
+int silc_dh_generate_private(SilcDH dh, SilcMPInt **x);
+
+/****f* silccrypt/SilcDH/silc_dh_compute_public
+ *
+ * SYNOPSIS
+ *
+ *    int silc_dh_compute_public(SilcDH dh, 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. 
+ *
+ ***/
+int silc_dh_compute_public(SilcDH dh, SilcMPInt **y);
+
+/****f* silccrypt/SilcDH/silc_dh_remote_public
+ *
+ * SYNOPSIS
+ *
+ *    int 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. 
+ *
+ ***/
+int silc_dh_set_remote_public(SilcDH dh, SilcMPInt *y);
+
+/****f* silccrypt/SilcDH/silc_dh_compute_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_dh_compute_key(SilcDH dh, 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. 
+ *
+ ***/
+int silc_dh_compute_key(SilcDH dh, SilcMPInt **z);
+
+/****f* silccrypt/SilcDH/silc_dh_remote_public
+ *
+ * SYNOPSIS
+ *
+ *    int 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. 
+ *
+ ***/
+int silc_dh_compute_key_data(SilcDH dh, unsigned char **z, 
+                            uint32 *z_len);
+
+#endif
index 6b5f4c42842f887f2a6b9cfad0a7d93ef0a45051..4d0d27062d1f0485f4d5f81f5b0b677f2e2b2459 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;
+  SilcHashObject *new;
 
-  SILC_LOG_DEBUG(("Registering new hash function"));
+  SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
 
   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;
-  }
-
-  /* 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;
-  }
-
-  h = silc_hash_list;
-  while (h) {
-    if (!h->next) {
-      h->next = new;
-      break;
-    }
-    h = h->next;
-  }
+  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;
+  if (!silc_hash_list)
+    return FALSE;
 
-  /* 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;
-    }
+  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;
+      }
 
-  /* Unregister the hash function */
-  if (h->hash == hash) {
-    tmp = h->next;
-    silc_free(h->hash->name);
-    silc_free(h);
-    silc_hash_list = tmp;
-
-    return TRUE;
-  }
-
-  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 +142,164 @@ 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)
+{
+  char fingerprint[64], *cp;
+  unsigned char h[32];
+  int i;
+
+  if (!hash)
+    silc_hash_alloc("sha1", &hash);
+
+  silc_hash_make(hash, data, data_len, h);
+  
+  memset(fingerprint, 0, sizeof(fingerprint));
+  cp = fingerprint;
+  for (i = 0; i < hash->hash->hash_len; i++) {
+    snprintf(cp, sizeof(fingerprint), "%02X", h[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);
+}
+
+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)
+{
+  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", &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];
+
+  return babbleprint;
+}
index 98f85ef47d0da1846c6e61f2131b8886858e2aad..8feb8e3402e4d7d0188ef3aabe27b0244b5d3252 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 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..77dce315b4693961ee662c34300b4eb7dac9f15b 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 }
+};
 
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac)
+static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
+                                   uint32 key_len)
 {
-  SILC_LOG_DEBUG(("Allocating new hmac object"));
+  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));
+
+  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;
+
+  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)
+{
+  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_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_hmac_make_internal(hmac, data, data_len, hmac->key, 
-                         hmac->key_len, return_hash);
+  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 +326,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..624921691d31fad015d569ac03678e3136929ec8 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 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..866662b3e911256f58047c8d363a992c2be503f7 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,125 @@ 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)
     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 +176,931 @@ 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 self)
 {
   return self->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. */
+
+uint32 silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk,
+                                    uint32 pk_len)
+{
+  pkcs->key_len = pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len);
+  return pkcs->key_len;
+}
+
+/* Sets private key from SilcPrivateKey. */
 
-int silc_pkcs_set_public_key(SilcPKCS pkcs, unsigned char *pk, 
-                            unsigned int pk_len)
+int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key)
 {
-  return pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len);
+  return pkcs->pkcs->set_private_key(pkcs->context, private_key->prv, 
+                                    private_key->prv_len);
 }
 
-/* Sets private key */
+/* Sets private key from data. */
 
-int silc_pkcs_set_private_key(SilcPKCS pkcs, unsigned char *prv, 
-                             unsigned int prv_len)
+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;
+}
+
+/* Verifies signature with hash. The `data' is hashed and verified against
+   the `signature'. */
 
-int silc_pkcs_save_public_key(SilcPKCS pkcs, char *filename,
-                             unsigned char *pk, unsigned int pk_len)
+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); 
+  }
 
-  buf = silc_buffer_alloc(strlen(pkcs->pkcs->name) + 2 + pk_len
-                         + strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) 
-                         + strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
+  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. */
+
+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;
   }
 
- out:
   silc_buffer_free(buf);
+  return TRUE;
+
+ 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. */
 
-int silc_pkcs_save_private_key(SilcPKCS pkcs, char *filename,
-                              unsigned char *prv, unsigned int prv_len,
-                              char *passphrase)
+unsigned char *
+silc_pkcs_private_key_data_encode(unsigned char *prv, uint32 prv_len,
+                                 char *pkcs, uint32 *len)
 {
   SilcBuffer buf;
-  int ret = TRUE;
+  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;
 
-  buf = silc_buffer_alloc(strlen(pkcs->pkcs->name) + 2 + prv_len
-                         + strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) 
-                         + strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+  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 */
+
+static int silc_pkcs_save_public_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_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);
+      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..5f291328f6a6037bcc3baee641278b3d8bc84ba6 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 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..3c2dea539a1d6c1dc957f7d5487f689798d53423 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"
 
+#ifdef HAVE_GETSID
+extern __pid_t getsid (__pid_t __pid);
+#endif
+
+#ifdef HAVE_GETPGID
+extern __pid_t getpgid (__pid_t __pid);
+#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 +62,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 +75,8 @@
 
 */
 typedef struct SilcRngStateContext {
-  unsigned int low;
-  unsigned int pos;
+  uint32 low;
+  uint32 pos;
   struct SilcRngStateContext *next;
 } *SilcRngState;
 
@@ -95,12 +110,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,16 +136,15 @@ 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);
 
+  new->devrandom = strdup("/dev/random");
+
   return new;
 }
 
@@ -132,7 +155,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 +179,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 +202,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 +274,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;
@@ -287,15 +350,15 @@ void silc_rng_exec_command(SilcRng rng, char *command)
   /* Add the buffer into random pool */
   silc_rng_add_noise(rng, buf, strlen(buf));
   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 +375,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 +384,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 +421,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 +433,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 +442,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 +477,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 +493,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..c4b956f
--- /dev/null
@@ -0,0 +1,17 @@
+<!--
+@LIBRARY=SILC Math Library
+@FILENAME=silcmathlib.html
+@LINK=silcmath.html:SILC Math API
+@LINK=silcmp.html:SILC MP API
+-->
+
+<FONT SIZE="+3">SILC Math Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+SILC Math Library provides arbitrary precision artichmetic routines for
+public key cryptosystems, prime number generation routines, and other
+math utility functions for applications.
+</FONT>
+</PRE>
+
+@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..b8f1cda
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+
+  mp_gmp.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 <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..f3ae30e
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+
+  mp_mpi.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 "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)
+{
+  return mp_radix_size(mp, base) - 2; /* XXX This is actually wrong since
+                                        this might produce wrong balue.
+                                        But, it looks like MPI always returns
+                                        correct value + plus, whereas
+                                        GMP returns always the right value. */
+}
+
+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);
+}
diff --git a/lib/silcmath/mp_mpi.h b/lib/silcmath/mp_mpi.h
new file mode 100644 (file)
index 0000000..54614a3
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+
+  mp_mpi.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 MP_MPI_H
+#define MP_MPI_H
+
+#include "mpi.h"
+#include "mplogic.h"
+
+#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..45d6ff6704256330af59d31781b6bce41d9a9625 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_alloc
+ *
+ * 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_free
+ *
+ * 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..7380f0d
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+@LIBRARY=SILC SFTP Library
+@FILENAME=silcsftplib.html
+@LINK=silcsftp.html:SILC SFTP API
+@LINK=silcsftp_fs.html:SILC SFTP Filesystems
+-->
+
+<FONT SIZE="+3">SILC SFTP Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+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.
+</FONT>
+</PRE>
+
+@LINKS@
diff --git a/lib/silcsftp/Makefile.am b/lib/silcsftp/Makefile.am
new file mode 100644 (file)
index 0000000..e05612e
--- /dev/null
@@ -0,0 +1,36 @@
+#
+#  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
+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..29ee207
--- /dev/null
@@ -0,0 +1,1145 @@
+/*
+
+  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 {
+  uint32 id;
+  SilcSFTPPacket type;
+  SilcSFTPStatusCallback status;
+  SilcSFTPHandleCallback handle;
+  SilcSFTPDataCallback data;
+  SilcSFTPNameCallback name;
+  SilcSFTPAttrCallback attr;
+  SilcSFTPExtendedCallback extended;
+  void *context;
+} *SilcSFTPRequest;
+
+/* SFTP client context */
+typedef struct {
+  SilcSocketConnection sock;
+  SilcSFTPSendPacketCallback send_packet;
+  void *send_context;
+  SilcSFTPVersionCallback version;
+  void *version_context;
+  uint32 id;
+  SilcDList requests;
+} *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 packet;
+  va_list vp;
+
+  va_start(vp, len);
+  packet = silc_sftp_packet_encode_vp(type, len, vp);
+  va_end(vp);
+
+  if (!packet)
+    return;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to server"), packet->data, packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
+
+  silc_buffer_free(packet);
+}
+
+/* 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_dlist_start(sftp->requests);
+  while ((req = silc_dlist_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_dlist_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;
+  sftp->requests = silc_dlist_init();
+
+  /* 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;
+
+  silc_dlist_uninit(sftp->requests);
+  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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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_dlist_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..04979b7
--- /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 *)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) {
+    (*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\r
+               if (!lstat(entry->data + 7, &stats))
+#else\r
+               if (!stat(entry->data + 7, &stats))\r
+#endif\r
+                       filesize = stats.st_size;\r
+
+    /* 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\r
+  ret = lstat(entry->data + 7, &stats);
+#else\r
+  ret = stat(entry->data + 7, &stats);\r
+#endif\r
+  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..119dbf1
--- /dev/null
@@ -0,0 +1,1022 @@
+/*
+
+  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;
+} *SilcSFTPServer;
+
+/* General routine to send SFTP packet to the SFTP client. */
+
+static void silc_sftp_send_packet(SilcSFTPServer sftp,
+                                 SilcSFTPPacket type, 
+                                 uint32 len, ...)
+{
+  SilcBuffer packet;
+  va_list vp;
+
+  va_start(vp, len);
+  packet = silc_sftp_packet_encode_vp(type, len, vp);
+  va_end(vp);
+
+  if (!packet)
+    return;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to client"), packet->data, packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
+
+  silc_buffer_free(packet);
+}
+
+/* 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));
+
+  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..eb4652a
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+
+  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. */
+
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, uint32 len, ...)
+{
+  SilcBuffer buffer;
+  va_list vp;
+
+  va_start(vp, len);
+  buffer = silc_sftp_packet_encode_vp(packet, 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, uint32 len, 
+                                     va_list vp)
+{
+  SilcBuffer buffer;
+  int ret;
+
+  buffer = silc_buffer_alloc(4 + 1 + len);
+  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) {
+    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:
+  case ELOOP:
+    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..ac43b06
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+
+  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. */
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, 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, 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..3ab4714
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+
+  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, SilcSFTP sftp,
+ *                                    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,
+ *                                    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..196b676
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+
+  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);
+  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);
+  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 void packet_parse(SilcPacketParserContext *parser)
+{
+  Client client = (Client)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_client_receive_process(client->sftp, sock, packet);
+}
+
+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, NULL, NULL, 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;
+
+  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..e2aa1aa
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+
+  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);
+  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);
+  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 void packet_parse(SilcPacketParserContext *parser)
+{
+  Server server = (Server)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_server_receive_process(server->sftp[sock->sock], sock, packet);
+}
+
+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, NULL, NULL, 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..fdb14dc55f7b35f786a590a861f766787e981dd2 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 \
@@ -39,9 +43,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 +55,30 @@ 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
+       $(LN_S) $(srcdir)/../silccrypt/$*.c $*.c
        $(COMPILE) $(SIM_CFLAGS) $*.c -o $(SIM_MODULES_DIR)/$*.sim.so
        $(LN_S) $(srcdir)/$(SIM_MODULES_DIR)/$*.sim.so $*.o
        rm -rf $*.c
 
 $(SIM_HASH_OBJS): ../silccrypt/libsilccrypt.a
        rm -rf $*.c $*.o
-       $(LN_S) $(srcdir)/../silccrypt/$*.c
+       $(LN_S) $(srcdir)/../silccrypt/$*.c $*.c
        $(COMPILE) $(SIM_CFLAGS) $*.c -o $(SIM_MODULES_DIR)/$*.sim.so
        $(LN_S) $(srcdir)/$(SIM_MODULES_DIR)/$*.sim.so $*.o
        rm -rf $*.c
 
 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..d57c8bf
--- /dev/null
@@ -0,0 +1,17 @@
+<!--
+@LIBRARY=SILC Key Exchange Library
+@FILENAME=silcskelib.html
+@LINK=silcske.html:SILC SKE API
+@LINK=silcske_status.html:SILC SKE Status
+-->
+
+<FONT SIZE="+3">SILC Key Exchange Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+SILC Key Exchange (SKE) Library, is an implementation of the SKE protocol.
+It provides the key exchange protocol for all SILC applications.
+</FONT>
+</PRE>
+
+@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..0c83343dc4b945aea813b00f31b9448c6392eadf 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,43 +29,43 @@ 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 }
 };
@@ -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;
   }
@@ -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;
   }
@@ -167,3 +160,10 @@ 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;
+}
index 29e33be4d080d74a4141c677355ce26c7c24c1a5..1e36c5dec556bf1c22e35dff542cd137807f2fb5 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
@@ -22,7 +22,6 @@
 #define GROUPS_H
 
 #include "silcske_status.h"
-#include "groups_internal.h"
 
 /* Forward declaration */
 typedef struct SilcSKEDiffieHellmanGroupStruct *SilcSKEDiffieHellmanGroup;
@@ -36,5 +35,6 @@ SilcSKEStatus silc_ske_get_group_by_number(int number,
 SilcSKEStatus silc_ske_get_group_by_name(const char *name,
                                         SilcSKEDiffieHellmanGroup *ret);
 char *silc_ske_get_supported_groups();
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group);
 
 #endif
index 015c125eed8cd2911d9d12f9b171bedf4a100380..ab2787b4751fe3a5fcbff79aef3d7fc6bd812e03 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,9 @@ struct SilcSKEDiffieHellmanGroupDefStruct {
 struct SilcSKEDiffieHellmanGroupStruct {
   int number;
   char *name;
-  SilcInt group;
-  SilcInt group_order;
-  SilcInt generator;
+  SilcMPInt group;
+  SilcMPInt group_order;
+  SilcMPInt generator;
 };
 
 #endif
index 45c9c7cc5c0fb0661cd6345ff09f35c740fd9d22..d9e89389ed29d4e9f03a0eac3efadd0567c483a8 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;
 }
 
@@ -252,6 +160,8 @@ void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
   if (payload) {
     if (payload->cookie)
       silc_free(payload->cookie);
+    if (payload->version)
+      silc_free(payload->version);
     if (payload->ke_grp_list)
       silc_free(payload->ke_grp_list);
     if (payload->pkcs_alg_list)
@@ -260,268 +170,157 @@ void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
       silc_free(payload->enc_alg_list);
     if (payload->hash_alg_list)
       silc_free(payload->hash_alg_list);
+    if (payload->hmac_alg_list)
+      silc_free(payload->hmac_alg_list);
     if (payload->comp_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;
@@ -533,21 +332,23 @@ SilcSKEStatus silc_ske_payload_two_decode(SilcSKE ske,
     silc_free(payload->pk_data);
   if (payload->sign_data)
     silc_free(payload->sign_data);
+  if (x)
+    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);
+    silc_mp_uninit(&payload->x);
     if (payload->sign_data)
       silc_free(payload->sign_data);
-    silc_mp_clear(&payload->f);
     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..1e25f5541d15f6889fa7f708819d406dbe195efe 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,13 +66,9 @@ 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 */
-    if (ske->ke2_payload)
-      silc_ske_payload_two_free(ske->ke2_payload);
+      silc_ske_payload_ke_free(ske->ke1_payload);
 
     /* Free rest */
     if (ske->prop) {
@@ -76,37 +80,94 @@ void silc_ske_free(SilcSKE ske)
        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->x) {
+      silc_mp_uninit(ske->x);
+      silc_free(ske->x);
+    }
+    if (ske->KEY) {
+      silc_mp_uninit(ske->KEY);
+      silc_free(ske->KEY);
+    }
     if (ske->hash)
       silc_free(ske->hash);
     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 +185,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 +203,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 +214,23 @@ 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;
+  }
+
+  /* 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 
@@ -183,11 +259,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;
 
@@ -203,149 +285,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;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* 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;
+  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(("Computing KEY = f ^ x mod p"));
+  /* 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;
 
-  /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &payload->f, &ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
+  ske->users--;
+  payload = ske->ke2_payload;
 
-  SILC_LOG_DEBUG(("Verifying public key"));
+  /* 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;
+  }
 
-  /* 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"));
+  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;
+    }
 
-  /* Compute the hash value */
-  status = silc_ske_make_hash(ske, hash, &hash_len);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+    SILC_LOG_DEBUG(("Public key is authentic"));
 
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
-  SILC_LOG_DEBUG(("Verifying signature"));
+    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+    memcpy(ske->hash, hash, hash_len);
+    ske->hash_len = hash_len;
 
-  /* 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(("Verifying signature (HASH)"));
 
-    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"));
-
-  memset(hash, 'F', hash_len);
+  ske->status = SILC_SKE_STATUS_OK;
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+  /* Call the callback. The caller may now continue the SKE protocol. */
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->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 +505,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 +608,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 +622,33 @@ 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);
 
   return status;
 
@@ -411,6 +661,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 +669,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"));
 
@@ -457,21 +706,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_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -479,154 +736,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 +1036,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 (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS, 
+                                  ske->callbacks->context);
 
-  if (send_packet)
-    (*send_packet)(ske, packet, SILC_PACKET_SUCCESS, context);
+  silc_buffer_free(packet);
 
-  return status;
+  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 +1070,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 +1091,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 +1131,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 +1156,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 +1169,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, ',')) {
@@ -972,6 +1404,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 +1508,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 +1519,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 +1529,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 +1550,144 @@ 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);
+    
+    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));
+
+    /* 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->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);
+    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 +1703,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_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 +1746,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 +1763,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_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 +1806,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..b83779c
--- /dev/null
@@ -0,0 +1,34 @@
+<!--
+@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
+-->
+
+<FONT SIZE="+3">SILC Utility Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+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.
+</FONT>
+</PRE>
+
+@LINKS@
diff --git a/lib/silcutil/Makefile.am b/lib/silcutil/Makefile.am
new file mode 100644 (file)
index 0000000..93cd4a3
--- /dev/null
@@ -0,0 +1,61 @@
+#
+#  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
+
+if SILC_WIN32
+SUBDIRS=win32
+else
+SUBDIRS=unix
+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    \
+       silcutil.h
+endif
+
+EXTRA_DIST = *.h
+
+include $(top_srcdir)/Makefile.defines.in
similarity index 79%
rename from lib/silccore/silcbuffer.h
rename to lib/silcutil/silcbuffer.h
index 179c3d0e0567065917c4d402e077eef0887ad61d..a6ca1897e2fc3444e7650aa4566874a2da303b04 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
 
 */
 
-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 */
+
+extern 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 */
+
+extern 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. */
+
+extern 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
@@ -163,11 +191,13 @@ typedef SilcBufferObject *SilcBuffer;
 */
 
 extern inline 
-unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len)
+unsigned char *silc_buffer_pull(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_data = sb->data;
 
+#ifdef SILC_DEBUG
   assert(len <= (sb->tail - sb->data));
+#endif
 
   sb->data += len;
   sb->len -= len;
@@ -193,11 +223,13 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len)
 */
 
 extern inline 
-unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len)
+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;
@@ -223,11 +255,13 @@ unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len)
 */
 
 extern inline 
-unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len)
+unsigned char *silc_buffer_pull_tail(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_tail = sb->tail;
 
+#ifdef SILC_DEBUG
   assert((sb->end - sb->tail) >= len);
+#endif
 
   sb->tail += len;
   sb->len += len;
@@ -253,11 +287,13 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len)
 */
 
 extern inline
-unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len)
+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;
@@ -279,10 +315,12 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len)
 extern inline
 unsigned char *silc_buffer_put_head(SilcBuffer sb, 
                                    unsigned char *data,
-                                   unsigned int len)
+                                   uint32 len)
 {
+#ifdef SILC_DEBUG
   assert((sb->data - sb->head) >= len);
-  return memcpy(sb->head, data, len);
+#endif
+  return (unsigned char *)memcpy(sb->head, data, len);
 }
 
 /* Puts data at the start of the valid data area. Returns a pointer 
@@ -299,10 +337,12 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
 extern inline
 unsigned char *silc_buffer_put(SilcBuffer sb, 
                               unsigned char *data,
-                              unsigned int len)
+                              uint32 len)
 {
+#ifdef SILC_DEBUG
   assert((sb->tail - sb->data) >= len);
-  return memcpy(sb->data, data, len);
+#endif
+  return (unsigned char *)memcpy(sb->data, data, len);
 }
 
 /* Puts data at the tail of the buffer. Returns pointer to the copied
@@ -319,31 +359,12 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
 extern inline
 unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
                                    unsigned char *data,
-                                   unsigned int len)
+                                   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((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 68%
rename from lib/silccore/silcbufutil.h
rename to lib/silcutil/silcbufutil.h
index acbbcaf47cc1b5a7b278f2ae88437dd3ec55957f..7016b71e6ab231607cbeef4e048fbb9e26abdcb7 100644 (file)
@@ -21,8 +21,7 @@
 #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. */
@@ -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
+extern 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
similarity index 89%
rename from lib/silccore/silcconfig.c
rename to lib/silcutil/silcconfig.c
index 4e0d3d4810fcfe2562c9c88fd8615b4d4f0a1d34..19ae0b975b8ce0569f2c4dfc2951e7fd5f3108f1 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"
 
@@ -34,9 +27,9 @@
 void silc_config_open(char *filename, SilcBuffer *ret_buffer)
 {
   char *buffer;
-  int filelen;
+  uint32 filelen;
 
-  buffer = silc_file_read(filename, &filelen);
+  buffer = silc_file_readfile(filename, &filelen);
   if (buffer == NULL)
     return;
 
@@ -62,7 +55,6 @@ int silc_config_get_token(SilcBuffer buffer, char **dest)
     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);
diff --git a/lib/silcutil/silchashtable.c b/lib/silcutil/silchashtable.c
new file mode 100644 (file)
index 0000000..130d9fa
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+
+  silchashtable.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.
+
+*/
+/* 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;
+  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) {
+      if (compare((*entry)->key, key, compare_user_context))
+       foreach((*entry)->key, (*entry)->context, foreach_user_context);
+      entry = &(*entry)->next;
+    }
+  } else {
+    while (*entry) {
+      if ((*entry)->key == key)
+       foreach((*entry)->key, (*entry)->context, foreach_user_context);
+      entry = &(*entry)->next;
+    }
+  }
+}
+
+/* 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;
+
+  if (!foreach)
+    return;
+
+  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;
+    }
+  }
+}
+
+/* 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;
+}
+
+/* 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..bdee0e1
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+
+  silchashtable.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/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))
+ *      ...
+ *
+ * SOURCE
+ */
+typedef struct SilcHashTableListStruct SilcHashTableList;
+
+/* List structure to traverse the hash table. */
+struct SilcHashTableListStruct {
+  SilcHashTable ht;
+  void *entry;
+  uint32 index;
+};
+/***/
+
+/****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.
+ *
+ ***/
+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.
+ *
+ ***/
+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.
+ *
+ ***/
+void silc_hash_table_list(SilcHashTable ht, 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.
+ *
+ ***/
+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/silclog.c b/lib/silcutil/silclog.c
new file mode 100644 (file)
index 0000000..15c6488
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+
+  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$ */
+
+#include "silcincludes.h"
+
+/* Set TRUE/FALSE to enable/disable debugging */
+int silc_debug = FALSE;
+
+/* 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;
+uint32 log_info_size;
+uint32 log_warning_size;
+uint32 log_error_size;
+uint32 log_fatal_size;
+
+/* Log callbacks. If these are set by the application these are used
+   instead of the default functions in this file. */
+static SilcLogCb info_cb = NULL;
+static SilcLogCb warning_cb = NULL;
+static SilcLogCb error_cb = NULL;
+static SilcLogCb fatal_cb = NULL;
+
+/* Debug callbacks. If set these are used instead of default ones. */
+static SilcDebugCb debug_cb = NULL;
+static SilcDebugHexdumpCb debug_hexdump_cb = NULL;
+
+/* Outputs the log message to what ever log file selected. */
+
+void silc_log_output(const char *filename, uint32 maxsize,
+                     SilcLogType type, char *string)
+{
+  FILE *fp;
+  const SilcLogTypeName *np;
+
+  switch(type)
+    {
+    case SILC_LOG_INFO:
+      if (info_cb) {
+       (*info_cb)(string);
+       silc_free(string);
+       return;
+      }
+      break;
+    case SILC_LOG_WARNING:
+      if (warning_cb) {
+       (*warning_cb)(string);
+       silc_free(string);
+       return;
+      }
+      break;
+    case SILC_LOG_ERROR:
+      if (error_cb) {
+       (*error_cb)(string);
+       silc_free(string);
+       return;
+      }
+      break;
+    case SILC_LOG_FATAL:
+      if (fatal_cb) {
+       (*fatal_cb)(string);
+       silc_free(string);
+       return;
+      }
+      break;
+    }
+
+  if (!filename)
+    fp = stderr;
+  else {
+    /* 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 (filelen >= maxsize)
+         unlink(filename);
+       
+       fclose(fp);
+      }
+    }
+    
+    /* 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);
+  if (fp != stderr)
+    fclose(fp);
+  silc_free(string);
+}
+
+/* Outputs the debug message to stderr. */
+
+void silc_log_output_debug(char *file, char *function, 
+                          int line, char *string)
+{
+  if (!silc_debug) {
+    silc_free(string);
+    return;
+  }
+
+  if (debug_cb)
+    {
+      (*debug_cb)(file, function, line, string);
+      silc_free(string);
+      return;
+    }
+
+  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,
+                            uint32 len, char *string)
+{
+  int i, k;
+  int off, pos, count;
+  unsigned char *data = (unsigned char *)data_in;
+
+  if (!silc_debug) {
+    silc_free(string);
+    return;
+  }
+
+  if (debug_hexdump_cb)
+    {
+      (*debug_hexdump_cb)(file, function, line, data_in, len, string);
+      silc_free(string);
+      return;
+    }
+
+  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, uint32 info_size, 
+                       char *warning, uint32 warning_size,
+                       char *error, uint32 error_size,
+                       char *fatal, uint32 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;
+}
+
+/* Sets log callbacks */
+
+void silc_log_set_callbacks(SilcLogCb info, SilcLogCb warning,
+                           SilcLogCb error, SilcLogCb fatal)
+{
+  info_cb = info;
+  warning_cb = warning;
+  error_cb = error;
+  fatal_cb = fatal;
+}
+
+/* Resets log callbacks */
+
+void silc_log_reset_callbacks()
+{
+  info_cb = warning_cb = error_cb = fatal_cb = NULL;
+}
+
+/* Sets debug callbacks */
+
+void silc_log_set_debug_callbacks(SilcDebugCb debug, 
+                                 SilcDebugHexdumpCb debug_hexdump)
+{
+  debug_cb = debug;
+  debug_hexdump_cb = debug_hexdump;
+}
+
+/* Resets debug callbacks */
+
+void silc_log_reset_debug_callbacks()
+{
+  debug_cb = NULL;
+  debug_hexdump_cb = NULL;
+}
similarity index 64%
rename from lib/silccore/silclog.h
rename to lib/silcutil/silclog.h
index 3cfc499a5f02ce3909bb922dccb881b5be7dd1f4..05ed479ba8a176af980228fb6882b62efd0facc2 100644 (file)
@@ -21,6 +21,9 @@
 #ifndef SILCLOG_H
 #define SILCLOG_H
 
+/* Set TRUE/FALSE to enable/disable debugging */
+extern int silc_debug;
+
 /* SILC Log types */
 typedef enum {
   SILC_LOG_INFO,
@@ -35,6 +38,18 @@ typedef struct {
   SilcLogType type;
 } SilcLogTypeName;
 
+/* Log function callback. */
+typedef void (*SilcLogCb)(char *message);
+
+/* Debug function callback. */
+typedef void (*SilcDebugCb)(char *file, char *function, 
+                           int line, char *message);
+
+/* Debug hexdump function callback. */
+typedef void (*SilcDebugHexdumpCb)(char *file, char *function, 
+                                  int line, unsigned char *data,
+                                  uint32 data_len, char *message);
+
 /* Default log filenames */
 #define SILC_LOG_FILE_INFO "silcd.log"
 #define SILC_LOG_FILE_WARNING "silcd_error.log"
@@ -46,28 +61,32 @@ 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;
+extern uint32 log_info_size;
+extern uint32 log_warning_size;
+extern uint32 log_error_size;
+extern uint32 log_fatal_size;
+
+#ifdef WIN32
+#define __FUNCTION__ ""
+#endif
 
 /* Log macros. */
-#define SILC_LOG_INFO(fmt) silc_log_output(log_info_file, \
+#define SILC_LOG_INFO(fmt) (silc_log_output(log_info_file, \
                                            log_info_size, \
                                           SILC_LOG_INFO, \
-                                          silc_log_format fmt)) 
+                                          silc_format fmt))
 #define SILC_LOG_WARNING(fmt) (silc_log_output(log_warning_file, \
                                                log_warning_size, \
                                               SILC_LOG_WARNING, \
-                                              silc_log_format fmt))
+                                              silc_format fmt))
 #define SILC_LOG_ERROR(fmt) (silc_log_output(log_error_file, \
                                              log_error_size, \
                                             SILC_LOG_ERROR, \
-                                            silc_log_format fmt))
+                                            silc_format fmt))
 #define SILC_LOG_FATAL(fmt) (silc_log_output(log_fatal_file, \
                                              log_fatal_size, \
                                             SILC_LOG_FATAL, \
-                                            silc_log_format fmt))
+                                            silc_format fmt))
 
 /* Debug macro is a bit different from other logging macros and it
    is compiled in only if debugging is enabled. */
@@ -75,30 +94,35 @@ extern unsigned int log_fatal_size;
 #define SILC_LOG_DEBUG(fmt) (silc_log_output_debug(__FILE__, \
                                                   __FUNCTION__, \
                                                   __LINE__, \
-                                                  silc_log_format fmt))
+                                                  silc_format fmt))
 #define SILC_LOG_HEXDUMP(fmt, data, len) \
   (silc_log_output_hexdump(__FILE__, \
                           __FUNCTION__, \
                           __LINE__, \
                            (data), (len), \
-                          silc_log_format fmt))
+                          silc_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,
+void silc_log_output(const char *filename, uint32 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);
+                             uint32 len, char *string);
+void silc_log_set_files(char *info, uint32 info_size, 
+                       char *warning, uint32 warning_size,
+                       char *error, uint32 error_size,
+                        char *fatal, uint32 fatal_size);
+void silc_log_set_callbacks(SilcLogCb info, SilcLogCb warning,
+                           SilcLogCb error, SilcLogCb fatal);
+void silc_log_reset_callbacks();
+void silc_log_set_debug_callbacks(SilcDebugCb debug, 
+                                 SilcDebugHexdumpCb debug_hexdump);
+void silc_log_reset_debug_callbacks();
 
 #endif
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..24cd562
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+
+  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 IP address. */
+
+bool silc_net_is_ip(const char *addr)
+{
+  struct in_addr tmp;
+  int len = sizeof(tmp);
+  return silc_net_addr2bin(addr, (unsigned char *)&tmp.s_addr, len);
+}
+
+/* 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)
+{
+  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);
+}
+
+/* Return local port by socket. */
+
+uint16 silc_net_get_local_port(int sock)
+{
+  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);
+}
+
+/* Return name of localhost. */
+
+char *silc_net_localhost(void)
+{
+  char hostname[256];
+  struct hostent *dest;
+
+  if (gethostname(hostname, sizeof(hostname)))
+    return NULL;
+
+  dest = gethostbyname(hostname);
+  if (!dest)
+    return strdup(hostname);
+
+  return strdup(dest->h_name);
+}
+
+/* Returns local IP address */
+
+char *silc_net_localip(void)
+{
+  char hostname[256];
+  struct hostent *dest;
+  struct in_addr ip;
+  char *ips;
+
+  if (gethostname(hostname, sizeof(hostname)))
+    return NULL;
+
+  dest = gethostbyname(hostname);
+  if (!dest)
+    return NULL;
+
+  memcpy(&ip.s_addr, dest->h_addr_list[0], 4);
+  ips = inet_ntoa(ip);
+
+  return strdup(ips);
+}
diff --git a/lib/silcutil/silcnet.h b/lib/silcutil/silcnet.h
new file mode 100644 (file)
index 0000000..124fcbf
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+
+  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, 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_ip
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_is_ip(const char *addr);
+ *
+ * DESCRIPTION
+ *
+ *    Checks whether IP address sent as argument is valid IP address.
+ *
+ ***/
+bool silc_net_is_ip(const char *addr);
+
+/****f* silcutil/SilcNetAPI/silc_net_addr2bin
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_addr2bin(const char *addr, unsigned char *bin,
+ *                           uint32 bin_len);
+ *
+ * DESCRIPTION
+ *
+ *    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);
+
+/****f* silcutil/SilcNetAPI/silc_net_addr2bin_ne
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_addr2bin_ne(const char *addr, unsigned char *bin,
+ *                             uint32 bin_len);
+ *
+ * DESCRIPTION
+ *
+ *    Converts the IP number string from numbers-and-dots notation to
+ *    binary form in network byte order.
+ *
+ ***/
+bool silc_net_addr2bin_ne(const char *addr, unsigned char *bin,
+                         uint32 bin_len);
+
+/****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..8a8e581
--- /dev/null
@@ -0,0 +1,1209 @@
+/*
+
+  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"));
+  silc_mutex_lock(schedule->lock);
+  schedule->valid = FALSE;
+  silc_mutex_unlock(schedule->lock);
+}
+
+/* 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..8ae2cd4
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+  
+  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).
+ *
+ ***/
+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..942b30d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+
+  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);
+    }
+
+    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);
+
+#ifdef SILC_THREADS
+  silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
+#else
+  silc_socket_host_lookup_start((void *)lookup);
+#endif
+}
diff --git a/lib/silcutil/silcsockconn.h b/lib/silcutil/silcsockconn.h
new file mode 100644 (file)
index 0000000..f2e15e3
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+  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;
+  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_read(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/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..cbc072b
--- /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);
+ *
+ * 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..c61e70c
--- /dev/null
@@ -0,0 +1,906 @@
+/*
+
+  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$ */
+
+#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\r
+  ret = lstat(filename, &stats);
+#else\r
+  ret = stat(filename, &stats);\r
+#endif\r
+  if (ret < 0)\r
+    return 0;\r
+
+  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 (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;
+      struct in_addr ipv4;
+
+      if (server_id->ip.data_len > 4) {
+
+      } else {
+       SILC_GET32_MSB(ipv4.s_addr, server_id->ip.data);
+       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;
+      struct in_addr ipv4;
+
+      if (client_id->ip.data_len > 4) {
+
+      } else {
+       SILC_GET32_MSB(ipv4.s_addr, client_id->ip.data);
+       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;
+      struct in_addr ipv4;
+
+      if (channel_id->ip.data_len > 4) {
+
+      } else {
+       SILC_GET32_MSB(ipv4.s_addr, channel_id->ip.data);
+       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 = 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 < 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);
+}
diff --git a/lib/silcutil/silcutil.h b/lib/silcutil/silcutil.h
new file mode 100644 (file)
index 0000000..1c467a5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+
+  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 */
+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);
+
+#endif
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 */
similarity index 59%
rename from lib/silccore/silcnet.c
rename to lib/silcutil/unix/silcunixnet.c
index b35c18271511b57ae44c20b028b515ba7fa56281..cc8a26b63ff38a8f89af9d65739a88b7dcaef98f 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcnet.c
+  silcunixnet.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 "silcnet.h"
@@ -39,11 +32,12 @@ int silc_net_create_server(int port, char *ip_addr)
 {
   int sock, 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);
+  sock = socket(AF_INET, SOCK_STREAM, 0);
   if (sock < 0) {
     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
     return -1;
@@ -58,19 +52,21 @@ int silc_net_create_server(int port, char *ip_addr)
 
   /* Set the socket information for bind() */
   memset(&server, 0, sizeof(server));
-  server.sin_family = PF_INET;
-  server.sin_port = htons(port);
+  server.sin_family = AF_INET;
+  if (port)
+    server.sin_port = htons(port);
 
   /* Convert IP address to network byte order */
-  if (ip_addr)
-    inet_aton(ip_addr, &server.sin_addr);
+  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: %s", strerror(errno)));
+    SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
     return -1;
   }
 
@@ -89,6 +85,8 @@ int silc_net_create_server(int port, char *ip_addr)
   return sock;
 }
 
+/* Closes the server by closing the socket connection. */
+
 void silc_net_close_server(int sock)
 {
   shutdown(sock, 2);
@@ -101,7 +99,8 @@ void silc_net_close_server(int sock)
    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 silc_net_create_connection(const char *local_ip, int port, 
+                              const char *host)
 {
   int sock, rval;
   struct hostent *dest;
@@ -112,22 +111,46 @@ int silc_net_create_connection(int port, char *host)
   /* Do host lookup */
   dest = gethostbyname(host);
   if (!dest) {
-    SILC_LOG_ERROR(("Network (%s) unreachable", host));
+    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(PF_INET, SOCK_STREAM, 0);
+  sock = socket(AF_INET, 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) {
+    struct sockaddr_in local;
+    int local_len = sizeof(local.sin_addr);
+
+    /* Set the socket information for bind() */
+    memset(&local, 0, sizeof(local));
+    local.sin_family = AF_INET;
+
+    /* Convert IP address to network byte order */
+    silc_net_addr2bin(local_ip, (unsigned char *)&local.sin_addr.s_addr, 
+                     local_len);
+
+    /* Bind the local socket */
+    rval = bind(sock, (struct sockaddr *)&local, sizeof(local));
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot connect to remote host: "
+                     "cannot bind socket: %s", strerror(errno)));
+      return -1;
+    }
+  }
+
   /* Connect to the host */
   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
   if (rval < 0) {
@@ -137,8 +160,11 @@ int silc_net_create_connection(int port, char *host)
     return -1;
   }
 
-  /* Set appropriate option */
+  /* 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"));
 
@@ -150,7 +176,8 @@ int silc_net_create_connection(int port, char *host)
    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 silc_net_create_connection_async(const char *local_ip, int port, 
+                                    const char *host)
 {
   int sock, rval;
   struct hostent *dest;
@@ -162,22 +189,46 @@ int silc_net_create_connection_async(int port, char *host)
   /* Do host lookup */
   dest = gethostbyname(host);
   if (!dest) {
-    SILC_LOG_ERROR(("Network (%s) unreachable", host));
+    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(PF_INET, SOCK_STREAM, 0);
+  sock = socket(AF_INET, 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) {
+    struct sockaddr_in local;
+    int local_len = sizeof(local.sin_addr);
+
+    /* Set the socket information for bind() */
+    memset(&local, 0, sizeof(local));
+    local.sin_family = AF_INET;
+
+    /* Convert IP address to network byte order */
+    silc_net_addr2bin(local_ip, (unsigned char *)&local.sin_addr.s_addr, 
+                     local_len);
+
+    /* Bind the local socket */
+    rval = bind(sock, (struct sockaddr *)&local, sizeof(local));
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot connect to remote host: "
+                     "cannot bind socket: %s", strerror(errno)));
+      return -1;
+    }
+  }
+
   /* Set the socket to non-blocking mode */
   silc_net_set_socket_nonblock(sock);
 
@@ -192,28 +243,24 @@ int silc_net_create_connection_async(int port, char *host)
     }
   }
 
-  /* Set appropriate option */
+  /* 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 */
+/* Closes the connection by closing the socket 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)
@@ -221,73 +268,38 @@ 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. */
+/* Converts the IP number string from numbers-and-dots notation to
+   binary form. */
 
-int silc_net_set_socket_opt(int sock, int level, int option, int on)
+bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len)
 {
-  return setsockopt(sock, level, option, (void *)&on, sizeof(on));
+  struct in_addr tmp;
+  int ret;
+
+  ret = inet_aton(addr, &tmp);
+
+  if (bin_len < 4)
+    return FALSE;
+
+  memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
+  return ret != 0;
 }
 
-/* Checks whether IP address sent as argument is valid IP address. */
+/* Converts the IP number string from numbers-and-dots notation to
+   binary form in network byte order. */
 
-int silc_net_is_ip(const char *addr)
+bool silc_net_addr2bin_ne(const char *addr, unsigned char *bin,
+                         uint32 bin_len)
 {
   struct in_addr tmp;
-  return inet_aton(addr, &tmp);
-}
+  int ret;
 
-/* Performs lookups for remote name and IP address. */
+  ret = inet_aton(addr, &tmp);
 
-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));
+  if (bin_len < 4)
+    return FALSE;
+
+  SILC_PUT32_MSB(tmp.s_addr, bin);
+
+  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..3120e8e
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+
+  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)));
+      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)));
+    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;
+}
diff --git a/lib/silcutil/unix/silcunixthread.c b/lib/silcutil/unix/silcunixthread.c
new file mode 100644 (file)
index 0000000..7751603
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+
+  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"
+
+#ifdef SILC_THREADS
+
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable)
+{
+  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;
+}
+
+void silc_thread_exit(void *exit_value)
+{
+  pthread_exit(exit_value);
+}
+
+SilcThread silc_thread_self(void)
+{
+  pthread_t self = pthread_self();
+  return (SilcThread)self;
+}
+
+bool silc_thread_wait(SilcThread thread, void **exit_value)
+{
+  SILC_LOG_DEBUG(("Waiting for thread %p", thread));
+  if (!pthread_join(*(pthread_t *)thread, exit_value))
+    return TRUE;
+  return FALSE;
+}
+
+#endif /* SILC_THREADS */
diff --git a/lib/silcutil/unix/silcunixutil.c b/lib/silcutil/unix/silcunixutil.c
new file mode 100644 (file)
index 0000000..696adb3
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+
+  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] == ',') {
+      regex[count] = '|';
+      count++;
+      continue;
+    }
+
+    regex[count] = string[i];
+    count++;
+  }
+
+  regex[count - 1] = ')';
+  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;
+
+  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..e8fcc26
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+
+  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$ */
+
+#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)
+{
+  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 *)&tmp.s_addr, 4);
+  return ret != INADDR_NONE;
+}
+
+/* Converts the IP number string from numbers-and-dots notation to
+   binary form in network byte order. */
+
+bool silc_net_addr2bin_ne(const char *addr, unsigned char *bin,
+                         uint32 bin_len)
+{
+  unsigned long ret;
+
+  ret = inet_addr(addr);
+
+  if (bin_len < 4)
+    return FALSE;
+
+  SILC_PUT32_MSB(ret, bin);
+
+  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..7f604c0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+
+  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));
+      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));
+    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;
+}
diff --git a/lib/silcutil/win32/silcwin32thread.c b/lib/silcutil/win32/silcwin32thread.c
new file mode 100644 (file)
index 0000000..e88a09f
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+
+  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;
+}
+
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable)
+{
+  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;
+}
+
+void silc_thread_exit(void *exit_value)
+{
+  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);
+}
+
+SilcThread silc_thread_self(void)
+{
+  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;
+}
+
+bool silc_thread_wait(SilcThread thread, void **exit_value)
+{
+  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;
+}
+
+#endif /* SILC_THREADS */
diff --git a/lib/silcutil/win32/silcwin32util.c b/lib/silcutil/win32/silcwin32util.c
new file mode 100644 (file)
index 0000000..afe441f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+
+  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"
+
+#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/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..8942bf63a9b14518ca8a8d7214ffdb33e1f84715 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.6.1                     # 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 trq"
+cd lib/trq
+aclocal
+autoconf
+autoheader </dev/null 2>/dev/null
+automake >/dev/null 2>/dev/null
+cd ../..
+
+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 "Done, now run ./configure and make."
index 3c8754b71c45345cbed423c840ef995e86fb32cc..ee9567c6b42736048f77017efbdfcd74f5050757 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 ltmain.sh
+rm -f ltconfig
+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 -f lib/trq/tests/Makefile
+rm -f lib/trq/trq_test/Makefile
+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/contribute.php b/public_html/html/contribute.php
new file mode 100644 (file)
index 0000000..6a576df
--- /dev/null
@@ -0,0 +1,21 @@
+&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>
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..d56955a
--- /dev/null
@@ -0,0 +1,188 @@
+&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="blue">
+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="blue">
+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="blue">
+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="blue">
+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="blue">
+./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="blue">
+./silc -d -f configfile 2&gt;log<br />
+./silcd -d -f configfile 2&gt;log
+</tt>
+
+<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="blue">
+./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..2ee3217
--- /dev/null
@@ -0,0 +1,135 @@
+&nbsp;<br />
+<b><big>SILC Documentation</big></b>
+<br />&nbsp;<br />
+
+README file from packages: <a href="docs/README" class="normal">README</a>
+<br />
+Coding Style in SILC source tree: <a href="docs/CodingStyle" class="normal">CodingStyle</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><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
+<a href="http://www.ietf.org/" class="normal">IETF</a>.
+<br />&nbsp;<br />
+<b>NOTE Mon Nov  5 18:53:56 EET 2001:</b> New versions of the protocol 
+specifications are almost ready  for submission to the IETF. You can 
+preview the upcoming versions <a href="docs/drafts/">here</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-03.txt" class="normal">
+draft-riikonen-silc-spec-03.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-03.txt" class="normal">
+draft-riikonen-silc-pp-03.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-03.txt" class="normal">
+draft-riikonen-silc-ke-auth-03.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-01.txt" class="normal">
+draft-riikonen-silc-commands-01.txt</a>
diff --git a/public_html/html/download.php b/public_html/html/download.php
new file mode 100644 (file)
index 0000000..8bddde3
--- /dev/null
@@ -0,0 +1,111 @@
+&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 />
+
+<b>SILC Client <?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 />
+Sources HTTP:
+<a href="download/silc-client-<?php echo $Latest_Client; ?>.tar.gz" class="normal">
+tar.gz</a> (<?php echo
+div(FileSize("download/silc-client-".$Latest_Client.".tar.gz"),1024); ?> kB),
+<a href="download/silc-client-<?php echo $Latest_Client; ?>.tar.bz2" class="normal">
+tar.bz2</a> (<?php echo
+div(FileSize("download/silc-client-".$Latest_Client.".tar.bz2"),1024); ?> kB)
+<br />
+
+Sources FTP: <a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/" 
+class="normal">tar.gz and tar.bz2</a>
+<br />
+
+Binaries HTTP: 
+<a href="download/silc-client-<?php echo $Latest_RPM_Client; ?>.i386.rpm" 
+class="normal">RPM</a> (<?php echo 
+div(FileSize("download/silc-client-".$Latest_RPM_Client.".i386.rpm"),1024); 
+?> kB)
+, <a href="download/silc-<?php echo $Latest_Windows_Client; ?>.exe.zip" 
+class="normal">Cygwin</a> (<?php 
+echo div(FileSize("download/silc-".$Latest_Windows_Client.".exe.zip"),1024); ?> kB)
+, <a href="download/SILCclie-<?php echo $Latest_Solaris_Client; ?>-sol8-sparc-local.gz" 
+class="normal">Solaris 8/SPARC</a> (<?php
+echo div(FileSize("download/SILCclie-".$Latest_Solaris_Client."-sol8-sparc-local.gz"),1024); ?> kB)
+
+<br />
+
+Binaries FTP: <a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/rpm/" 
+class="normal">RPM</a>, <a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/solaris/" 
+class="normal">Solaris 8/SPARC</a>
+<br />&nbsp;<br />
+
+
+<b>SILC Server <?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 />
+Sources HTTP:
+<a href="download/silc-server-<?php echo $Latest_Server; ?>.tar.gz" class="normal">
+tar.gz</a> (<?php echo
+div(FileSize("download/silc-server-".$Latest_Server.".tar.gz"),1024); ?> kB),
+<a href="download/silc-server-<?php echo $Latest_Server; ?>.tar.bz2" class="normal">
+tar.bz2</a> (<?php echo
+div(FileSize("download/silc-server-".$Latest_Server.".tar.bz2"),1024); ?> kB)
+<br />
+Sources FTP: <a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/" class="normal">tar.gz and tar.bz2</a>
+<br />&nbsp;<br />
+
+<b>SILC Toolkit <?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 />
+Sources HTTP:
+<a href="download/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.gz" class="normal">
+tar.gz</a> (<?php echo
+div(FileSize("download/silc-toolkit-".$Latest_Toolkit.".tar.gz"),1024); ?> kB),
+<a href="download/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.bz2" class="normal">
+tar.bz2</a> (<?php echo
+div(FileSize("download/silc-toolkit-".$Latest_Toolkit.".tar.bz2"),1024); ?> kB)
+<br />
+Sources FTP: <a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/" class="normal">tar.gz and tar.bz2</a>
+<br />
+Binaries HTTP:<a href="download/silc-toolkit-<?php echo $Latest_Toolkit_Win32 ?>.zip" class="normal">
+Win32</a> (<?php echo
+div(FileSize("download/silc-toolkit-".$Latest_Toolkit_Win32.".zip"),1024); ?> kB)
+<br />&nbsp;<br />
+
+<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 />
+HTTP: <a href="download/silc.tar.gz" class="normal">CVS Snapshot</a>
+<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.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>
diff --git a/public_html/html/faq.php b/public_html/html/faq.php
new file mode 100644 (file)
index 0000000..61c215d
--- /dev/null
@@ -0,0 +1,385 @@
+&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_60" class="normal">1.7 Where can I find more information?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_70" class="normal">1.8 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_47" class="normal">2.6 I am behind a firewall, can I use SILC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_50" class="normal">2.7 How secure SILC really is?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_60" class="normal">2.8 Does SILC support instant messaging?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_70" class="normal">2.9 Why SILC does not have LINKS command like in IRC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_80" class="normal">2.10 Why SILC does not have STATS command like in IRC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_90" class="normal">2.11 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;<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 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;<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 />
+<br />&nbsp;<br />
+
+<a name="f1_0"></a>
+<b>1. General Questions</b><br />&nbsp;<br />
+
+<a name="f1_10"></a>
+<samp class="blue">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="blue">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="blue">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="blue">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="blue">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="blue">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_60"></a>
+<samp class="blue">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="blue">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="blue">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.
+<br />&nbsp;<br />
+
+<a name="f2_20"></a>
+<samp class="blue">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="blue">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="blue">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="blue">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_47"></a>
+<samp class="blue">Q: I am behind a firewall, can I use SILC?</samp><br />
+A: Yes. If your network administrator can open the 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="blue">Q: How secure SILC really is?</samp><br />
+A: A good question which I don't have an answer for. 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.
+<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. SILC is vulnerable to this attack if the public
+keys used in the SILC are not verified to be trusted (as any other
+protocol for that matter).<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.
+<br />&nbsp;<br />
+
+<a name="f2_60"></a>
+<samp class="blue">Q: Does SILC support instant messaing?</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 called an Instant Messaging 
+system.
+<br />&nbsp;<br />
+
+<a name="f2_70"></a>
+<samp class="blue">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="blue">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="blue">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.
+<br />&nbsp;<br />
+
+
+<a name="f3_0"></a><br />
+<b>3. Client Questions</b><br />&nbsp;<br />
+
+<a name="f3_10"></a>
+<samp class="blue">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="blue">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="f4_0"></a><br />
+<b>4. Server Questions</b><br />&nbsp;<br />
+
+<a name="f4_10"></a>
+<samp class="blue">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="blue">Q: Can I run 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="blue">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="blue">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="blue">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="f5_0"></a><br />
+<b>5. Toolkit Questions</b><br />&nbsp;<br />
+
+<a name="f5_10"></a>
+<samp class="blue">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="blue">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="blue">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="blue">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 />
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/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..a193c27
--- /dev/null
@@ -0,0 +1,17 @@
+&nbsp;<br />
+<tt class="black">
+<?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/lists.php b/public_html/html/lists.php
new file mode 100644 (file)
index 0000000..e6b965c
--- /dev/null
@@ -0,0 +1,53 @@
+&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>
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..aa15679
--- /dev/null
@@ -0,0 +1,71 @@
+&nbsp;<br />
+<b><big>SILC Client <?php echo $Latest_Client; ?> Is Now Available!</big></b>
+<br /><small class="blue"><?php echo $Date_Client ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Client; ?> of SILC Client is available!
+Read the README and INSTALL files after downloading for instructions how 
+to compile and use 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 />&nbsp;<br />&nbsp;<br />
+
+<b><big>SILC Server <?php echo $Latest_Server; ?> Is Now Available!</big></b>
+<br /><small class="blue"><?php echo $Date_Server ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Server; ?> of SILC Server is available!
+Read the README and INSTALL files after downloading for instructions how 
+to compile and use 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 />&nbsp;<br />&nbsp;<br />
+
+<b><big>SILC Toolkit <?php echo $Latest_Toolkit; ?> Is Now Available!</big></b>
+<br /><small class="blue"><?php echo $Date_Toolkit ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Toolkit; ?> of 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 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 />&nbsp;<br />&nbsp;<br />
+
+<b><big>Try out SILC server at silc.silcnet.org</big></b>
+<br />&nbsp;<br />
+You are 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 
+give command <i>/server silc.silcnet.org</i>. You might also want to try 
+out one of the following servers:
+<br />&nbsp;<br />
+
+- <samp class="blue">silc.silcnet.org (router)</samp> (Slovakia)<br />
+- <samp class="blue">silc.ytti.fi</samp> (Finland)<br />
+- <samp class="blue">silc.peelo.com</samp> (Finland)<br />
+- <samp class="blue">silc.debolaz.com</samp> (Norway)<br />
+- <samp class="blue">silc.highertechnology.com</samp> (USA)<br />
+- <samp class="blue">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/todo.php b/public_html/html/todo.php
new file mode 100644 (file)
index 0000000..a050763
--- /dev/null
@@ -0,0 +1,17 @@
+&nbsp;<br />
+<tt class="black">
+<?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..bb1ac3c
--- /dev/null
@@ -0,0 +1,857 @@
+&nbsp;<br />
+<b><big>SILC Protocol White Paper</big>
+<br />
+Version 1.0 / 03 Aug 2001
+
+<br />&nbsp;<br />
+Introduction</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<b>About This White Paper</b><br />&nbsp;<br />
+
+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" class="normal">Terms and Abbreviations</a> for terms used
+in this document.
+
+<br />&nbsp;<br />
+(c) Copyright 2001 Pekka Riikonen 
+(<a href="mailto:priikone at silcnet.org" class="normal">priikone at silcnet.org</a>)
+<br />&nbsp;<br />
+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.
+
+
+<br />&nbsp;<br />
+<b>SILC Protocol</b><br />&nbsp;<br />
+
+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 and various commands.  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.
+<br />&nbsp;<br />
+
+Generally it is assumed that the SILC Network is trusted.  This means
+that clients can fully trust the servers and routers in the SILC Network.
+In real life this is not always possible.  In the Internet it is possible
+that some server or router would get compromised by a malicious
+cracker.  However, if the SILC Network is closed network, for example
+inside a orgranization the assumption generally is true.  The SILC
+protocol is secure even if the end users consider the network
+untrusted, and provides several ways to still have secure conversation
+on the SILC Network.
+<br />&nbsp;<br />
+
+The packets in the SILC network are always encrypted.  It is not possible
+to send unencrypted messages in SILC.  This assures that end user cannot
+even accidently send unencrypted messages while thinking that it is
+encrypted.  This is the problem of most 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.  In these cases the security is achieved usually by encrypting the
+data while key management 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.
+<br />&nbsp;<br />
+
+The network topology is also different to various other chat protocol,
+like for example IRC.  IRC has tree style network where SILC has so
+called cellular network.  A cell consists of a router, servers and clients.
+The cell can also have backup routers in case the private router becomes
+unresponsive.
+
+<br />&nbsp;<br />
+<img src="img/silc_network.png" alt="( SILC Network - IMAGE )" />
+<br />&nbsp;<br />
+
+The diagram above illustrates a portion of the SILC network.  It shows
+two cells that both has 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.
+
+<br />&nbsp;<br />
+<b>Clients</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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 perferred 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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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 authentication.
+
+
+<br />&nbsp;<br />
+<b>Servers</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Routers</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_routers.png" alt="( SILC Routers - IMAGE )" />
+<br />&nbsp;<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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>SILC Packet Protocol</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_packet.png" alt="( Typical SILC Packet - IMAGE )" />
+<br />&nbsp;<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.
+
+
+<br />&nbsp;<br />
+<b>SILC Key Exchange Protocol</b><br />&nbsp;<br />
+
+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 man-in-the-middle attack.
+<br />&nbsp;<br />
+
+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).
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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 responder also computes a signature
+that the initiator will verify.  It is also possible to perform a
+mutual authentication where both of the parties computes a signature
+which are verified by each other independently.  If any of the phases
+of the protocol are to fail the connection is closed immeadiately.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>SILC Connection Authentication Protocol</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Channels</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Channel Message Delivery</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_channel.png" alt="( Channel Message Delivery - IMAGE )" />
+<br />&nbsp;<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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Channel Message Delivery With Channel Private Key</b><br />&nbsp;<br />
+
+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.  Sservers 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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Private Messages</b><br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Private Message Delivery With Session Keys</b><br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_priv1.png" alt="( Basic Private Message Delivery - IMAGE )" />
+<br />&nbsp;<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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Private Message Delivery With Private Message Key</b><br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_priv2.png" alt="( Private Messages with Private Message Key - IMAGE )" />
+<br />&nbsp;<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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+<br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Private Message Delivery With Public Key Encryption</b><br />&nbsp;<br />
+
+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.
+
+<br />&nbsp;<br />
+<img src="img/silc_priv3.png" alt="( Private Messges with Public Key Cryptosystem - IMAGE )" />
+<br />&nbsp;<br />
+
+As the diagram above shows the Client A has the Client B's public key.
+It will encrypt the message with that key and sends the message to the
+SILC Network.  All servers and routers pass the message through since
+they cannot decrypt it.  The Client B then uses its private key to
+decrypt the message.  The Client B has also the Client A's public key 
+that it can use to encrypt messages that it will send to Client A.
+<br />&nbsp;<br />
+
+Even this method of private message delivery is not perfect.  The drawbacks
+of this method is that the public key encryption process, as being
+asymmetric cryptosystem, is much slower than encryption process with
+symmetric cryptosystems.  This is not probably problem with short messages
+but may be inconvenient with long messages.  The other drawback 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. 
+However, the servers may not have authenticated the fetched public key so
+that should not be fully trusted.  Use of certificates can solve the
+problem.  The receiver's certificate could be authenticated by a third
+party Certification Authority (CA).
+
+<br />&nbsp;<br />
+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 they trust that the keys are authentic.
+If both of the clients trust that the public keys are authentic using this
+method of private message delivery is very simple and recommended.
+
+
+<br />&nbsp;<br />
+<b>Conclusion</b><br />&nbsp;<br />
+
+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.
+
+
+<br />&nbsp;<br />
+<b>Further Information</b><br />&nbsp;<br />
+
+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/" class="normal">IETF website</a>.
+<br />&nbsp;<br />
+
+- <a href="docs/draft-riikonen-silc-spec-03.txt" class="normal">
+Secure Internet Live Conferencing (SILC), Protocol Specification</a>
+<br />
+- <a href="docs/draft-riikonen-silc-pp-03.txt" class="normal">
+SILC Packet Protocol</a>
+<br />
+- <a href="docs/draft-riikonen-silc-ke-auth-03.txt" class="normal">
+SILC Key Exchange and Authentication Protocols</a>
+<br />
+- <a href="docs/draft-riikonen-silc-commands-01.txt" class="normal">
+SILC Commands</a>
+<br />&nbsp;<br />
+
+For comprehensive introduction to cryptography refer to the
+<a href="http://www.ssh.com/tech/crypto/" class="normal">Cryptography A-2-Z document</a>.
+
+<br />&nbsp;<br />
+<a name="terms"></a>
+<b>Terms and Abbreviations</b><br />&nbsp;<br />
+
+- Asymmetric cryptosystem
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Authentication
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Certificate
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Certification Authority (CA)
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Diffie-Hellman key exchange
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Encryption
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Integrity
+<br />&nbsp;<br />
+The verification of data to detect any modifications.  If data is
+modified enroute from the sender to the receiver, the modification will
+be detected.
+<br />&nbsp;<br />
+
+- HMAC
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Key management
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Man-in-the-middle attack
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Message Authentication Code (MAC)
+<br />&nbsp;<br />
+MAC provides message integrity by computing the MAC using a secret
+key authentication algorithm (HMAC).
+<br />&nbsp;<br />
+
+- Perfect Forward Secrecy (PFS)
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Rekey
+<br />&nbsp;<br />
+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.
+<br />&nbsp;<br />
+
+- Symmetric cryptosystem
+<br />&nbsp;<br />
+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.
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..0bcd03e
--- /dev/null
@@ -0,0 +1,196 @@
+<?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/silc-toolkit-".$Latest_Toolkit.".tar.gz"));
+  $Date_Client  = date("l dS of F Y H:i:s",
+                  filemtime("download/silc-client-".$Latest_Client.".tar.gz"));
+  $Date_Server  = date("l dS of F Y H:i:s",
+                  filemtime("download/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="black">
+    <table width="100%" cellpadding="0" cellspacing="0" border="0">
+     <tr>
+      <td class="white">
+        <img src="img/silc.gif" width="700" height="100" alt=" " />
+      </td>
+     </tr>
+     <tr>
+      <td class="white" align="center">
+        <table cellspacing="3" cellpadding="10" border="0">
+        <tr><td valign="top" class="white">
+        <b>General</b><br />
+        <small class="black">o</small> <a href="?page=news" class="normal">SILC News</a><br />
+        <small class="black">o</small> <a href="?page=about" class="normal">About the SILC</a><br />
+        <small class="black">o</small> <a href="?page=history" class="normal">History of SILC</a><br />
+        <small class="black">o</small> <a href="?page=contribute" class="normal">Contributing</a><br />
+        <small class="black">o</small> <a href="?page=lists" class="normal">SILC Mailing Lists</a><br />
+        </td><td valign="top" class="white">
+        <b>Documentation</b><br />
+        <small class="black">o</small> <a href="?page=docs" class="normal">SILC Documentation</a><br />
+        <small class="black">o</small> <a href="?page=whitepaper" class="normal">SILC White Paper</a><br />
+        <small class="black">o</small> <a href="?page=faq" class="normal">SILC FAQ</a><br />
+        <small class="black">o</small> <a href="?page=features" class="normal">SILC Features</a><br />
+        <small class="black">o</small> <a href="?page=todo" class="normal">TODO List</a><br />
+        </td><td valign="top" class="white">
+        <b>Software</b><br />
+        <small class="black">o</small> <a href="?page=download" class="normal">Download SILC</a><br />
+        <small class="black">o</small> <a href="?page=mirrors" class="normal">Mirrors Worldwide</a><br />
+        <small class="black">o</small> <a href="?page=cvs" class="normal">Anonymous CVS Access</a><br />
+        <small class="black">o</small> <a href="txt/changes.txt" class="normal">ChangeLog</a><br />
+        <small class="black">o</small> <a href="?page=copying" class="normal">The General Public License (GPL)</a><br />
+        </td></tr></table>
+      </td>
+     </tr>
+     <tr><td class="blackline"></td></tr>
+     <tr>
+      <td class="<?php if($pass == 1 && $page == "whitepaper") $color="whitetext"; else $color="greytext"; 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;
+    }
+    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="blue">
+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..7b5b015
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+cat << EOF > tmp.php
+<?php \$page="$3"; \$dest="$1"; require "$2"; ?>
+EOF
+php -f tmp.php >$4.tmp
+mv $4.tmp $4
+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..1ab755b
--- /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>
+<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>
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..0ad58fa
--- /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 valign="top"><font face="Helvetica,Arial,Sans-serif">
+       <table width="100%" 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..b03eacb
--- /dev/null
@@ -0,0 +1,118 @@
+#!/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"`
+
+#
+# HTML documentation
+#
+if [ "$TYPE" = "HTML" ]; then
+  mkdir /tmp/silcdoc.html
+  cp $headers /tmp/silcdoc.html
+
+  # Generate indes template 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`
+    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">$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">$n</A><BR>" >>$DST/index.tmpl
+    done
+  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_toc.php $DST/$i.html $DST/$i.html
+    sh gen.sh $DST index.php $DST/$i.html $DST/$i.html
+
+    # Generate the details and the layour
+    files=`find $DST -name ""$i"_*.html"`
+    for k in $files
+    do
+      sh gen.sh $DST gen_detail.php $k $k
+      sh gen.sh $DST index.php $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_toc.php $DST/$fname $DST/$fname
+    sh gen.sh $DST index.php $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"`
+  curdate=`date`
+  sed -e "/@DATE@/s//$curdate/" -e "/@BODY@/ r $DST/index.html.tmp" -e s/@BODY@//g $index >$DST/index.html
+  sh gen.sh $DST gen_toc.php $DST/index.html $DST/index.html
+  sh gen.sh $DST index.php $DST/index.html $DST/index.html
+
+  rm -rf $DST/index.html.tmp
+  rm -rf /tmp/silcdoc.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..c9dcd8d
--- /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\">%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\">%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=\"+3\" COLOR=\"#000044\"><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=\"+3\" COLOR=\"#000044\"><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=\"+3\" COLOR=\"#000044\"><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=\"+3\" COLOR=\"#000044\"><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=\"#000044\">");
+      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..d00bf79
--- /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 trq_conf.h silc.dsw copy_dll
+
+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..44b20fb
--- /dev/null
@@ -0,0 +1,621 @@
+;
+; 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
+       log_error_file @ 42 ; 
+       log_error_size @ 43 ; 
+       log_fatal_file @ 44 ; 
+       log_fatal_size @ 45 ; 
+       log_info_file @ 46 ; 
+       log_info_size @ 47 ; 
+       log_warning_file @ 49 ; 
+       log_warning_size @ 50 ; 
+       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_mesage_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 ; 
+       silc_decode_pem @ 329 ; 
+       silc_default_ciphers @ 330 ; 
+       silc_default_hash @ 331 ; 
+       silc_default_hmacs @ 332 ; 
+       silc_default_pkcs @ 333 ; 
+       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_data @ 409 ; 
+       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_callbacks @ 444 ; 
+       silc_log_set_debug_callbacks @ 445 ; 
+       silc_log_set_files @ 446 ; 
+       silc_log_types @ 447 ; 
+       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_get_group_by_name @ 678 ; 
+       silc_ske_get_group_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 ; 
+       trq_deque_bubblesort @ 724 ; 
+       trq_deque_clear___func @ 725 ; 
+       trq_deque_compatible___func @ 726 ; 
+       trq_deque_empty_p___func @ 727 ; 
+       trq_deque_find @ 728 ; 
+       trq_deque_get_head___func @ 729 ; 
+       trq_deque_get_nth @ 730 ; 
+       trq_deque_get_tail___func @ 731 ; 
+       trq_deque_in_order @ 732 ; 
+       trq_deque_init_with_offset___func @ 733 ; 
+       trq_deque_insert_head___func @ 734 ; 
+       trq_deque_insert_in_order @ 735 ; 
+       trq_deque_insert_nth @ 736 ; 
+       trq_deque_insert_tail___func @ 737 ; 
+       trq_deque_insertionsort @ 738 ; 
+       trq_deque_join_head @ 739 ; 
+       trq_deque_join_tail @ 740 ; 
+       trq_deque_length___func @ 741 ; 
+       trq_deque_mapcar @ 742 ; 
+       trq_deque_mapcar_reverse @ 743 ; 
+       trq_deque_member @ 744 ; 
+       trq_deque_mergesort @ 745 ; 
+       trq_deque_quicksort @ 746 ; 
+       trq_deque_remove @ 747 ; 
+       trq_deque_reverse___func @ 748 ; 
+       trq_deque_swap @ 749 ; 
+       trq_deque_to_list @ 750 ; 
+       trq_list_bubblesort @ 751 ; 
+       trq_list_bw @ 752 ; 
+       trq_list_clear___func @ 753 ; 
+       trq_list_compatible___func @ 754 ; 
+       trq_list_current___func @ 755 ; 
+       trq_list_end___func @ 756 ; 
+       trq_list_find @ 757 ; 
+       trq_list_fw @ 758 ; 
+       trq_list_get_bw___func @ 759 ; 
+       trq_list_get_fw___func @ 760 ; 
+       trq_list_get_head___func @ 761 ; 
+       trq_list_get_nth @ 762 ; 
+       trq_list_get_tail___func @ 763 ; 
+       trq_list_in_order @ 764 ; 
+       trq_list_insert_head___func @ 765 ; 
+       trq_list_insert_in_order @ 766 ; 
+       trq_list_insert_nth @ 767 ; 
+       trq_list_insert_tail___func @ 768 ; 
+       trq_list_insertionsort @ 769 ; 
+       trq_list_join_head @ 770 ; 
+       trq_list_join_tail @ 771 ; 
+       trq_list_length___func @ 772 ; 
+       trq_list_mapcar @ 773 ; 
+       trq_list_mapcar_reverse @ 774 ; 
+       trq_list_member @ 775 ; 
+       trq_list_mergesort @ 776 ; 
+       trq_list_pointer_invalidate___func @ 777 ; 
+       trq_list_pointer_valid___func @ 778 ; 
+       trq_list_put_after___func @ 779 ; 
+       trq_list_put_before___func @ 780 ; 
+       trq_list_quicksort @ 781 ; 
+       trq_list_remove___func @ 782 ; 
+       trq_list_reverse @ 783 ; 
+       trq_list_rewind___func @ 784 ; 
+       trq_list_rewind_find @ 785 ; 
+       trq_list_rewind_to @ 786 ; 
+       trq_list_swap @ 787 ; 
+       trq_list_to_deque @ 788 ; 
+       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_net_addr2bin_ne @ 823 ;
+       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 ;
diff --git a/win32/libsilc/libsilc.dsp b/win32/libsilc/libsilc.dsp
new file mode 100644 (file)
index 0000000..f85c43d
--- /dev/null
@@ -0,0 +1,855 @@
+# 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" /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" /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\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\silcpayload.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 "trq"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_sort.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_001.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_002.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_003.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_004.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_005.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_006.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_007.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_008.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_009.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x_010.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_sort.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_001.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_002.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_003.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_004.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_005.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_006.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_007.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_008.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_009.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_010.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_011.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_012.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_013.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_014.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_015.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x_016.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\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\silcpayload.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\silchashtable.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 "trq No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\silcdlist.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\silclist.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_conf.h.sample\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_f.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_deque_x.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_f.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\trq\trq_list_x.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
+# Begin Source File\r
+\r
+SOURCE=..\trq_conf.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
diff --git a/win32/libsilcclient/Makefile.am b/win32/libsilcclient/Makefile.am
new file mode 100644 (file)
index 0000000..58a30e5
--- /dev/null
@@ -0,0 +1,24 @@
+#
+#  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
+
+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..8845650
--- /dev/null
@@ -0,0 +1,160 @@
+;\r
+; Exports file for SILC Client DLL.\r
+;\r
+; This file is generated from MinGW compiled object files using the\r
+; following command:\r
+;\r
+; dlltool --export-all --output-def libsilc.def libsilc.a\r
+;\r
+; ----------------------------------------------------------------------------\r
+;\r
+; If you edit this file by adding or removing any exports be sure to\r
+; preserve the ordinal values (the @ n in the exports) and add new exports\r
+; always at the end of the list with new ordinal value. Do not ever\r
+; add new export with old ordinal value if you need to preserve backwards\r
+; compatiblity. -Pekka\r
+;\r
+EXPORTS\r
+       silc_ske_check_version @ 1 ; \r
+       silc_client_add_channel_private_key @ 2 ; \r
+       silc_client_add_connection @ 3 ; \r
+       silc_client_add_private_message_key @ 4 ; \r
+       silc_client_add_private_message_key_ske @ 5 ; \r
+       silc_client_add_socket @ 6 ; \r
+       silc_client_alloc @ 7 ; \r
+       silc_client_channel_message @ 8 ; \r
+       silc_client_close_connection @ 9 ; \r
+       silc_client_command_alloc @ 10 ; \r
+       silc_client_command_ban @ 11 ; \r
+       silc_client_command_close @ 12 ; \r
+       silc_client_command_cmode @ 13 ; \r
+       silc_client_command_connect @ 14 ; \r
+       silc_client_command_cumode @ 15 ; \r
+       silc_client_command_dup @ 16 ; \r
+       silc_client_command_find @ 17 ; \r
+       silc_client_command_free @ 18 ; \r
+       silc_client_command_get_channel_by_id_callback @ 19 ; \r
+       silc_client_command_get_client_by_id_callback @ 20 ; \r
+       silc_client_command_get_client_callback @ 21 ; \r
+       silc_client_command_get_clients_list_callback @ 22 ; \r
+       silc_client_command_getkey @ 23 ; \r
+       silc_client_command_identify @ 24 ; \r
+       silc_client_command_info @ 25 ; \r
+       silc_client_command_invite @ 26 ; \r
+       silc_client_command_join @ 27 ; \r
+       silc_client_command_kick @ 28 ; \r
+       silc_client_command_kill @ 29 ; \r
+       silc_client_command_leave @ 30 ; \r
+       silc_client_command_list @ 31 ; \r
+       silc_client_command_motd @ 32 ; \r
+       silc_client_command_nick @ 33 ; \r
+       silc_client_command_oper @ 34 ; \r
+       silc_client_command_pending @ 35 ; \r
+       silc_client_command_pending_check @ 36 ; \r
+       silc_client_command_pending_del @ 37 ; \r
+       silc_client_command_ping @ 38 ; \r
+       silc_client_command_quit @ 39 ; \r
+       silc_client_command_reply_ban @ 40 ; \r
+       silc_client_command_reply_close @ 41 ; \r
+       silc_client_command_reply_cmode @ 42 ; \r
+       silc_client_command_reply_connect @ 43 ; \r
+       silc_client_command_reply_cumode @ 44 ; \r
+       silc_client_command_reply_free @ 45 ; \r
+       silc_client_command_reply_getkey @ 46 ; \r
+       silc_client_command_reply_identify @ 47 ; \r
+       silc_client_command_reply_info @ 48 ; \r
+       silc_client_command_reply_invite @ 49 ; \r
+       silc_client_command_reply_join @ 50 ; \r
+       silc_client_command_reply_kick @ 51 ; \r
+       silc_client_command_reply_kill @ 52 ; \r
+       silc_client_command_reply_leave @ 53 ; \r
+       silc_client_command_reply_list @ 54 ; \r
+       silc_client_command_reply_motd @ 55 ; \r
+       silc_client_command_reply_nick @ 56 ; \r
+       silc_client_command_reply_oper @ 57 ; \r
+       silc_client_command_reply_ping @ 58 ; \r
+       silc_client_command_reply_process @ 59 ; \r
+       silc_client_command_reply_shutdown @ 60 ; \r
+       silc_client_command_reply_silcoper @ 61 ; \r
+       silc_client_command_reply_topic @ 62 ; \r
+       silc_client_command_reply_umode @ 63 ; \r
+       silc_client_command_reply_users @ 64 ; \r
+       silc_client_command_reply_whois @ 65 ; \r
+       silc_client_command_reply_whowas @ 66 ; \r
+       silc_client_command_shutdown @ 67 ; \r
+       silc_client_command_silcoper @ 68 ; \r
+       silc_client_command_status_message @ 69 ; \r
+       silc_client_command_topic @ 70 ; \r
+       silc_client_command_umode @ 71 ; \r
+       silc_client_command_users @ 72 ; \r
+       silc_client_command_whois @ 73 ; \r
+       silc_client_command_whowas @ 74 ; \r
+       silc_client_connect_to_server @ 75 ; \r
+       silc_client_del_channel @ 76 ; \r
+       silc_client_del_channel_private_key @ 77 ; \r
+       silc_client_del_channel_private_keys @ 78 ; \r
+       silc_client_del_client @ 79 ; \r
+       silc_client_del_client_entry @ 80 ; \r
+       silc_client_del_connection @ 81 ; \r
+       silc_client_del_private_message_key @ 82 ; \r
+       silc_client_del_server @ 83 ; \r
+       silc_client_del_socket @ 84 ; \r
+       silc_client_disconnected_by_server @ 85 ; \r
+       silc_client_error_by_server @ 86 ; \r
+       silc_client_free @ 87 ; \r
+       silc_client_free_channel_private_keys @ 88 ; \r
+       silc_client_free_private_message_keys @ 89 ; \r
+       silc_client_get_channel @ 90 ; \r
+       silc_client_get_channel_by_id @ 91 ; \r
+       silc_client_get_channel_by_id_resolve @ 92 ; \r
+       silc_client_get_client_by_id @ 93 ; \r
+       silc_client_get_client_by_id_resolve @ 94 ; \r
+       silc_client_get_clients @ 95 ; \r
+       silc_client_get_clients_by_list @ 96 ; \r
+       silc_client_get_clients_local @ 97 ; \r
+       silc_client_get_server @ 98 ; \r
+       silc_client_get_server_by_id @ 99 ; \r
+       silc_client_init @ 100 ; \r
+       silc_client_key_agreement @ 101 ; \r
+       silc_client_list_channel_private_keys @ 102 ; \r
+       silc_client_list_private_message_keys @ 103 ; \r
+       silc_client_new_channel_id @ 104 ; \r
+       silc_client_notify_by_server @ 105 ; \r
+       silc_client_packet_process @ 106 ; \r
+       silc_client_packet_send @ 107 ; \r
+       silc_client_packet_send_real @ 108 ; \r
+       silc_client_perform_key_agreement @ 109 ; \r
+       silc_client_perform_key_agreement_fd @ 110 ; \r
+       silc_client_private_message @ 111 ; \r
+       silc_client_private_message_key @ 112 ; \r
+       silc_client_process_failure @ 113 ; \r
+       silc_client_protocol_ke_send_packet @ 114 ; \r
+       silc_client_protocol_ke_set_keys @ 115 ; \r
+       silc_client_protocol_ke_verify_key @ 116 ; \r
+       silc_client_protocols_register @ 117 ; \r
+       silc_client_protocols_unregister @ 118 ; \r
+       silc_client_receive_channel_key @ 119 ; \r
+       silc_client_receive_new_id @ 120 ; \r
+       silc_client_remove_from_channels @ 121 ; \r
+       silc_client_replace_from_channels @ 122 ; \r
+       silc_client_run @ 123 ; \r
+       silc_client_save_channel_key @ 124 ; \r
+       silc_client_send_channel_message @ 125 ; \r
+       silc_client_send_command @ 126 ; \r
+       silc_client_send_key_agreement @ 127 ; \r
+       silc_client_send_private_message @ 128 ; \r
+       silc_client_send_private_message_key @ 129 ; \r
+       silc_client_start_key_exchange @ 131 ; \r
+       silc_client_stop @ 132 ; \r
+       silc_command_list @ 133 ; \r
+       silc_command_reply_list @ 134 ; \r
+       silc_command_status_messages @ 135 ; \r
+       silc_idlist_get_channel_by_id @ 136 ; \r
+       silc_idlist_get_client @ 137 ; \r
+       silc_client_abort_key_agreement @ 138 ; \r
+       silc_client_set_away_message @ 139 ; \r
+       silc_client_request_authentication_method @ 140 ; \r
+       silc_client_file_send @ 141 ;\r
+       silc_client_file_receive @ 142 ;\r
+       silc_client_file_close @ 143 ;\r
+       \r
diff --git a/win32/libsilcclient/libsilcclient.dsp b/win32/libsilcclient/libsilcclient.dsp
new file mode 100644 (file)
index 0000000..29ab259
--- /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" /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" /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/trq_conf.h b/win32/trq_conf.h
new file mode 100644 (file)
index 0000000..21c895f
--- /dev/null
@@ -0,0 +1,62 @@
+/*   -*- c -*-\r
+ * \r
+ *  ----------------------------------------------------------------------\r
+ *  Deque for struct type with only one link pointer (x->next).\r
+ *  ----------------------------------------------------------------------\r
+ *  Created      : Fri Dec  5 11:19:37 1997 tri\r
+ *  Last modified: Thu Apr 16 17:35:57 1998 tri\r
+ *  ----------------------------------------------------------------------\r
+ *  Copyright © 1995-1998\r
+ *  Timo J. Rinne <tri@iki.fi>\r
+ *  All rights reserved.  See file COPYRIGHT for details.\r
+ * \r
+ *  Address: Cirion oy, PO-BOX 250, 00121 Helsinki, Finland\r
+ *  ----------------------------------------------------------------------\r
+ *  Any express or implied warranties are disclaimed.  In no event\r
+ *  shall the author be liable for any damages caused (directly or\r
+ *  otherwise) by the use of this software.\r
+ *\r
+ *  Please, send your patches to <tri@iki.fi>.\r
+ *  ----------------------------------------------------------------------\r
+ *\r
+ * $Id$\r
+ *\r
+ * $Log$
+ * Revision 1.1  2001/07/23 11:07:56  priikone
+ *     updates.
+ *\r
+ * Revision 1.1.1.1  2000/10/31 19:59:30  priikone\r
+ *     Imported TRQ and SilList and SilcDList API's.\r
+ *\r
+ * Revision 1.1  1998/04/16 14:39:42  tri\r
+ * Initial revision\r
+ *\r
+ *\r
+ */\r
+#ifndef __TRQ_CONF__H__\r
+#define __TRQ_CONF__H__ 1\r
+\r
+/*\r
+ * stddef.h is included here if such file exists.\r
+ * offsetof should be defined there.\r
+ */\r
+#include <stddef.h>\r
+\r
+/*\r
+ * If compiler supports inline functions, __TRQ__INLINE__FUNCTION__\r
+ * is defined to the correct keyword.  Usually this is defined\r
+ * as inline, __inline__ or __inline.  If inline functions are\r
+ * not supported, __TRQ__INLINE__FUNCTION__ is undefined.\r
+ */\r
+#define __TRQ__INLINE__FUNCTION__ __inline\r
+\r
+typedef unsigned long trq_p_i_t; /* Integral type size of an pointer */\r
+\r
+#ifdef offsetof\r
+#define _Q_STRUCT_OFFSET(t, m) ((trq_p_i_t)(offsetof(t, m)))\r
+#else\r
+#define _Q_STRUCT_OFFSET(t, m) (((trq_p_i_t)(&(((t *)0)->m))))\r
+#endif\r
+\r
+#endif /* !__TRQ_CONF__H__ */\r
+/* eof (trq_conf.h) */\r