Initial revision
authorPekka Riikonen <priikone@silcnet.org>
Tue, 27 Jun 2000 11:36:50 +0000 (11:36 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Tue, 27 Jun 2000 11:36:50 +0000 (11:36 +0000)
324 files changed:
COPYING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
apps/silc/Makefile.am [new file with mode: 0644]
apps/silc/client.c [new file with mode: 0644]
apps/silc/client.h [new file with mode: 0644]
apps/silc/clientconfig.c [new file with mode: 0644]
apps/silc/clientconfig.h [new file with mode: 0644]
apps/silc/clientutil.c [new file with mode: 0644]
apps/silc/clientutil.h [new file with mode: 0644]
apps/silc/command.c [new file with mode: 0644]
apps/silc/command.h [new file with mode: 0644]
apps/silc/command_reply.c [new file with mode: 0644]
apps/silc/command_reply.h [new file with mode: 0644]
apps/silc/idlist.h [new file with mode: 0644]
apps/silc/protocol.c [new file with mode: 0644]
apps/silc/protocol.h [new file with mode: 0644]
apps/silc/pubkey.pub [new file with mode: 0644]
apps/silc/screen.c [new file with mode: 0644]
apps/silc/screen.h [new file with mode: 0644]
apps/silc/silc.c [new file with mode: 0644]
apps/silc/silc.h [new file with mode: 0644]
apps/silc/testi.conf [new file with mode: 0644]
apps/silc/testi2.conf [new file with mode: 0644]
apps/silcd/Makefile.am [new file with mode: 0644]
apps/silcd/command.c [new file with mode: 0644]
apps/silcd/command.h [new file with mode: 0644]
apps/silcd/command_reply.c [new file with mode: 0644]
apps/silcd/command_reply.h [new file with mode: 0644]
apps/silcd/idlist.c [new file with mode: 0644]
apps/silcd/idlist.h [new file with mode: 0644]
apps/silcd/leevi.conf [new file with mode: 0644]
apps/silcd/leevi2.conf [new file with mode: 0644]
apps/silcd/protocol.c [new file with mode: 0644]
apps/silcd/protocol.h [new file with mode: 0644]
apps/silcd/pubkey.pub [new file with mode: 0644]
apps/silcd/route.c [new file with mode: 0644]
apps/silcd/route.h [new file with mode: 0644]
apps/silcd/server.c [new file with mode: 0644]
apps/silcd/server.h [new file with mode: 0644]
apps/silcd/server_internal.h [new file with mode: 0644]
apps/silcd/server_version.c [new file with mode: 0644]
apps/silcd/serverconfig.c [new file with mode: 0644]
apps/silcd/serverconfig.h [new file with mode: 0644]
apps/silcd/serverid.c [new file with mode: 0644]
apps/silcd/serverid.h [new file with mode: 0644]
apps/silcd/silc.conf [new file with mode: 0644]
apps/silcd/silcd.c [new file with mode: 0644]
apps/silcd/silcd.h [new file with mode: 0644]
apps/silcd/testi.conf [new file with mode: 0644]
apps/silcd/testi2.conf [new file with mode: 0644]
config.guess [new file with mode: 0755]
config.sub [new file with mode: 0755]
configure.in [new file with mode: 0644]
doc/CodingStyle [new file with mode: 0644]
doc/FAQ [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/Makefile.in [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-00.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-00.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-00.nroff [new file with mode: 0644]
doc/example_silc.conf [new file with mode: 0644]
doc/example_silcd.conf [new file with mode: 0644]
doc/fix.pl [new file with mode: 0755]
doc/makerfc [new file with mode: 0755]
includes/Makefile.am [new file with mode: 0644]
includes/Makefile.in [new file with mode: 0644]
includes/bitmove.h [new file with mode: 0644]
includes/clientincludes.h [new file with mode: 0644]
includes/serverincludes.h [new file with mode: 0644]
includes/silcdefs.h.in [new file with mode: 0644]
includes/silcincludes.h [new file with mode: 0644]
includes/version.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
lib/Makefile.am [new file with mode: 0644]
lib/silccore/Makefile.am [new file with mode: 0644]
lib/silccore/id.c [new file with mode: 0644]
lib/silccore/id.h [new file with mode: 0644]
lib/silccore/idcache.c [new file with mode: 0644]
lib/silccore/idcache.h [new file with mode: 0644]
lib/silccore/silcbuffer.c [new file with mode: 0644]
lib/silccore/silcbuffer.h [new file with mode: 0644]
lib/silccore/silcbuffmt.c [new file with mode: 0644]
lib/silccore/silcbuffmt.h [new file with mode: 0644]
lib/silccore/silcbufutil.c [new file with mode: 0644]
lib/silccore/silcbufutil.h [new file with mode: 0644]
lib/silccore/silcchannel.c [new file with mode: 0644]
lib/silccore/silcchannel.h [new file with mode: 0644]
lib/silccore/silccommand.c [new file with mode: 0644]
lib/silccore/silccommand.h [new file with mode: 0644]
lib/silccore/silcconfig.c [new file with mode: 0644]
lib/silccore/silcconfig.h [new file with mode: 0644]
lib/silccore/silclog.c [new file with mode: 0644]
lib/silccore/silclog.h [new file with mode: 0644]
lib/silccore/silcmemory.c [new file with mode: 0644]
lib/silccore/silcmemory.h [new file with mode: 0644]
lib/silccore/silcnet.c [new file with mode: 0644]
lib/silccore/silcnet.h [new file with mode: 0644]
lib/silccore/silcpacket.c [new file with mode: 0644]
lib/silccore/silcpacket.h [new file with mode: 0644]
lib/silccore/silcprotocol.c [new file with mode: 0644]
lib/silccore/silcprotocol.h [new file with mode: 0644]
lib/silccore/silcschedule.c [new file with mode: 0644]
lib/silccore/silcschedule.h [new file with mode: 0644]
lib/silccore/silcsockconn.c [new file with mode: 0644]
lib/silccore/silcsockconn.h [new file with mode: 0644]
lib/silccore/silctask.c [new file with mode: 0644]
lib/silccore/silctask.h [new file with mode: 0644]
lib/silccore/silcutil.c [new file with mode: 0644]
lib/silccore/silcutil.h [new file with mode: 0644]
lib/silccrypt/Makefile.am [new file with mode: 0644]
lib/silccrypt/blowfish.c [new file with mode: 0644]
lib/silccrypt/blowfish.h [new file with mode: 0644]
lib/silccrypt/blowfish_internal.h [new file with mode: 0644]
lib/silccrypt/cast.c [new file with mode: 0644]
lib/silccrypt/cast.h [new file with mode: 0644]
lib/silccrypt/cast_internal.h [new file with mode: 0644]
lib/silccrypt/ciphers.h [new file with mode: 0644]
lib/silccrypt/ciphers_def.h [new file with mode: 0644]
lib/silccrypt/crypton.c [new file with mode: 0644]
lib/silccrypt/crypton.h [new file with mode: 0644]
lib/silccrypt/crypton_internal.h [new file with mode: 0644]
lib/silccrypt/dfc.c [new file with mode: 0644]
lib/silccrypt/dfc.h [new file with mode: 0644]
lib/silccrypt/dfc_internal.h [new file with mode: 0644]
lib/silccrypt/e2.c [new file with mode: 0644]
lib/silccrypt/e2.h [new file with mode: 0644]
lib/silccrypt/e2_internal.h [new file with mode: 0644]
lib/silccrypt/loki.c [new file with mode: 0644]
lib/silccrypt/loki.h [new file with mode: 0644]
lib/silccrypt/loki_internal.h [new file with mode: 0644]
lib/silccrypt/mars.c [new file with mode: 0644]
lib/silccrypt/mars.h [new file with mode: 0644]
lib/silccrypt/mars_internal.h [new file with mode: 0644]
lib/silccrypt/md5.c [new file with mode: 0644]
lib/silccrypt/md5.h [new file with mode: 0644]
lib/silccrypt/md5_internal.h [new file with mode: 0644]
lib/silccrypt/none.c [new file with mode: 0644]
lib/silccrypt/none.h [new file with mode: 0644]
lib/silccrypt/rc5.c [new file with mode: 0644]
lib/silccrypt/rc5.h [new file with mode: 0644]
lib/silccrypt/rc5_internal.h [new file with mode: 0644]
lib/silccrypt/rc6.c [new file with mode: 0644]
lib/silccrypt/rc6.h [new file with mode: 0644]
lib/silccrypt/rc6_internal.h [new file with mode: 0644]
lib/silccrypt/rijndael.c [new file with mode: 0644]
lib/silccrypt/rijndael.h [new file with mode: 0644]
lib/silccrypt/rijndael_internal.h [new file with mode: 0644]
lib/silccrypt/rsa.c [new file with mode: 0644]
lib/silccrypt/rsa.h [new file with mode: 0644]
lib/silccrypt/rsa_internal.h [new file with mode: 0644]
lib/silccrypt/safer.c [new file with mode: 0644]
lib/silccrypt/safer.h [new file with mode: 0644]
lib/silccrypt/safer_internal.h [new file with mode: 0644]
lib/silccrypt/serpent.c [new file with mode: 0644]
lib/silccrypt/serpent.h [new file with mode: 0644]
lib/silccrypt/serpent_internal.h [new file with mode: 0644]
lib/silccrypt/sha1.c [new file with mode: 0644]
lib/silccrypt/sha1.h [new file with mode: 0644]
lib/silccrypt/sha1_internal.h [new file with mode: 0644]
lib/silccrypt/silccipher.c [new file with mode: 0644]
lib/silccrypt/silccipher.h [new file with mode: 0644]
lib/silccrypt/silchash.c [new file with mode: 0644]
lib/silccrypt/silchash.h [new file with mode: 0644]
lib/silccrypt/silchmac.c [new file with mode: 0644]
lib/silccrypt/silchmac.h [new file with mode: 0644]
lib/silccrypt/silcpkcs.c [new file with mode: 0644]
lib/silccrypt/silcpkcs.h [new file with mode: 0644]
lib/silccrypt/silcrng.c [new file with mode: 0644]
lib/silccrypt/silcrng.h [new file with mode: 0644]
lib/silccrypt/tests/inst [new file with mode: 0644]
lib/silccrypt/tests/inst_rsa [new file with mode: 0644]
lib/silccrypt/tests/inst_twofish [new file with mode: 0644]
lib/silccrypt/tests/insth [new file with mode: 0644]
lib/silccrypt/tests/test_mars.c [new file with mode: 0644]
lib/silccrypt/tests/test_rijndael.c [new file with mode: 0644]
lib/silccrypt/tests/test_rsa.c [new file with mode: 0644]
lib/silccrypt/tests/test_speed.c [new file with mode: 0644]
lib/silccrypt/tests/test_twofish.c [new file with mode: 0644]
lib/silccrypt/twofish.c [new file with mode: 0644]
lib/silccrypt/twofish.h [new file with mode: 0644]
lib/silccrypt/twofish_internal.h [new file with mode: 0644]
lib/silcmath/Makefile.am [new file with mode: 0644]
lib/silcmath/modinv.c [new file with mode: 0644]
lib/silcmath/modinv.h [new file with mode: 0644]
lib/silcmath/silcmp.h [new file with mode: 0644]
lib/silcmath/silcprimegen.c [new file with mode: 0644]
lib/silcmath/silcprimegen.h [new file with mode: 0644]
lib/silcsim/Makefile.am [new file with mode: 0644]
lib/silcsim/modules/Makefile.am [new file with mode: 0644]
lib/silcsim/modules/Makefile.in [new file with mode: 0644]
lib/silcsim/silcsim.c [new file with mode: 0644]
lib/silcsim/silcsim.h [new file with mode: 0644]
lib/silcsim/silcsimutil.c [new file with mode: 0644]
lib/silcsim/silcsimutil.h [new file with mode: 0644]
lib/silcske/Makefile.am [new file with mode: 0644]
lib/silcske/groups.c [new file with mode: 0644]
lib/silcske/groups.h [new file with mode: 0644]
lib/silcske/groups_internal.h [new file with mode: 0644]
lib/silcske/payload.c [new file with mode: 0644]
lib/silcske/payload.h [new file with mode: 0644]
lib/silcske/payload_internal.h [new file with mode: 0644]
lib/silcske/silcske.c [new file with mode: 0644]
lib/silcske/silcske.h [new file with mode: 0644]
lib/silcske/silcske_status.h [new file with mode: 0644]
lib/zlib/ChangeLog [new file with mode: 0644]
lib/zlib/FAQ [new file with mode: 0644]
lib/zlib/INDEX [new file with mode: 0644]
lib/zlib/Make_vms.com [new file with mode: 0644]
lib/zlib/Makefile [new file with mode: 0644]
lib/zlib/Makefile.in [new file with mode: 0644]
lib/zlib/Makefile.riscos [new file with mode: 0644]
lib/zlib/README [new file with mode: 0644]
lib/zlib/adler32.c [new file with mode: 0644]
lib/zlib/algorithm.txt [new file with mode: 0644]
lib/zlib/amiga/Makefile.pup [new file with mode: 0644]
lib/zlib/amiga/Makefile.sas [new file with mode: 0644]
lib/zlib/compress.c [new file with mode: 0644]
lib/zlib/configure [new file with mode: 0755]
lib/zlib/contrib/README.contrib [new file with mode: 0644]
lib/zlib/contrib/asm386/gvmat32.asm [new file with mode: 0644]
lib/zlib/contrib/asm386/gvmat32c.c [new file with mode: 0644]
lib/zlib/contrib/asm386/mkgvmt32.bat [new file with mode: 0644]
lib/zlib/contrib/asm386/zlibvc.def [new file with mode: 0644]
lib/zlib/contrib/asm386/zlibvc.dsp [new file with mode: 0644]
lib/zlib/contrib/asm386/zlibvc.dsw [new file with mode: 0644]
lib/zlib/contrib/asm586/README.586 [new file with mode: 0644]
lib/zlib/contrib/asm586/match.S [new file with mode: 0644]
lib/zlib/contrib/asm686/README.686 [new file with mode: 0644]
lib/zlib/contrib/asm686/match.S [new file with mode: 0644]
lib/zlib/contrib/delphi/zlib.mak [new file with mode: 0644]
lib/zlib/contrib/delphi/zlibdef.pas [new file with mode: 0644]
lib/zlib/contrib/delphi2/d_zlib.bpr [new file with mode: 0644]
lib/zlib/contrib/delphi2/d_zlib.cpp [new file with mode: 0644]
lib/zlib/contrib/delphi2/readme.txt [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib.bpg [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib.bpr [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib.cpp [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib.pas [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib32.bpr [new file with mode: 0644]
lib/zlib/contrib/delphi2/zlib32.cpp [new file with mode: 0644]
lib/zlib/contrib/iostream/test.cpp [new file with mode: 0644]
lib/zlib/contrib/iostream/zfstream.cpp [new file with mode: 0644]
lib/zlib/contrib/iostream/zfstream.h [new file with mode: 0644]
lib/zlib/contrib/iostream2/zstream.h [new file with mode: 0644]
lib/zlib/contrib/iostream2/zstream_test.cpp [new file with mode: 0644]
lib/zlib/contrib/minizip/ChangeLogUnzip [new file with mode: 0644]
lib/zlib/contrib/minizip/Makefile [new file with mode: 0644]
lib/zlib/contrib/minizip/miniunz.c [new file with mode: 0644]
lib/zlib/contrib/minizip/minizip.c [new file with mode: 0644]
lib/zlib/contrib/minizip/readme.txt [new file with mode: 0644]
lib/zlib/contrib/minizip/unzip.c [new file with mode: 0644]
lib/zlib/contrib/minizip/unzip.def [new file with mode: 0644]
lib/zlib/contrib/minizip/unzip.h [new file with mode: 0644]
lib/zlib/contrib/minizip/zip.c [new file with mode: 0644]
lib/zlib/contrib/minizip/zip.def [new file with mode: 0644]
lib/zlib/contrib/minizip/zip.h [new file with mode: 0644]
lib/zlib/contrib/minizip/zlibvc.def [new file with mode: 0644]
lib/zlib/contrib/minizip/zlibvc.dsp [new file with mode: 0644]
lib/zlib/contrib/minizip/zlibvc.dsw [new file with mode: 0644]
lib/zlib/contrib/untgz/Makefile [new file with mode: 0644]
lib/zlib/contrib/untgz/makefile.w32 [new file with mode: 0644]
lib/zlib/contrib/untgz/untgz.c [new file with mode: 0644]
lib/zlib/contrib/visual-basic.txt [new file with mode: 0644]
lib/zlib/crc32.c [new file with mode: 0644]
lib/zlib/deflate.c [new file with mode: 0644]
lib/zlib/deflate.h [new file with mode: 0644]
lib/zlib/descrip.mms [new file with mode: 0644]
lib/zlib/example.c [new file with mode: 0644]
lib/zlib/gzio.c [new file with mode: 0644]
lib/zlib/infblock.c [new file with mode: 0644]
lib/zlib/infblock.h [new file with mode: 0644]
lib/zlib/infcodes.c [new file with mode: 0644]
lib/zlib/infcodes.h [new file with mode: 0644]
lib/zlib/inffast.c [new file with mode: 0644]
lib/zlib/inffast.h [new file with mode: 0644]
lib/zlib/inffixed.h [new file with mode: 0644]
lib/zlib/inflate.c [new file with mode: 0644]
lib/zlib/inftrees.c [new file with mode: 0644]
lib/zlib/inftrees.h [new file with mode: 0644]
lib/zlib/infutil.c [new file with mode: 0644]
lib/zlib/infutil.h [new file with mode: 0644]
lib/zlib/maketree.c [new file with mode: 0644]
lib/zlib/minigzip.c [new file with mode: 0644]
lib/zlib/msdos/Makefile.b32 [new file with mode: 0644]
lib/zlib/msdos/Makefile.bor [new file with mode: 0644]
lib/zlib/msdos/Makefile.dj2 [new file with mode: 0644]
lib/zlib/msdos/Makefile.emx [new file with mode: 0644]
lib/zlib/msdos/Makefile.msc [new file with mode: 0644]
lib/zlib/msdos/Makefile.tc [new file with mode: 0644]
lib/zlib/msdos/Makefile.w32 [new file with mode: 0644]
lib/zlib/msdos/Makefile.wat [new file with mode: 0644]
lib/zlib/msdos/zlib.def [new file with mode: 0644]
lib/zlib/msdos/zlib.rc [new file with mode: 0644]
lib/zlib/nt/Makefile.emx [new file with mode: 0644]
lib/zlib/nt/Makefile.gcc [new file with mode: 0644]
lib/zlib/nt/Makefile.nt [new file with mode: 0644]
lib/zlib/nt/zlib.dnt [new file with mode: 0644]
lib/zlib/os2/Makefile.os2 [new file with mode: 0644]
lib/zlib/os2/zlib.def [new file with mode: 0644]
lib/zlib/trees.c [new file with mode: 0644]
lib/zlib/trees.h [new file with mode: 0644]
lib/zlib/uncompr.c [new file with mode: 0644]
lib/zlib/zconf.h [new file with mode: 0644]
lib/zlib/zlib.3 [new file with mode: 0644]
lib/zlib/zlib.h [new file with mode: 0644]
lib/zlib/ztest2188.c [new file with mode: 0644]
lib/zlib/ztest30104.c [new file with mode: 0644]
lib/zlib/zutil.c [new file with mode: 0644]
lib/zlib/zutil.h [new file with mode: 0644]
missing [new file with mode: 0755]
mkinstalldirs [new file with mode: 0755]
prepare [new file with mode: 0755]
prepare-clean [new file with mode: 0755]
public_html/about.html [new file with mode: 0644]
public_html/copying.html [new file with mode: 0644]
public_html/features.html [new file with mode: 0644]
public_html/history.html [new file with mode: 0644]
public_html/index.html [new file with mode: 0644]
public_html/silc.jpg [new file with mode: 0644]
public_html/silc2.jpg [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..c2d5f4b
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,23 @@
+Installing SILC Developer's Version
+===================================
+
+./configure
+./make
+
+You should not install the SILC into your system, instead, you should
+run it from the current directory.
+
+To see different compilation options, give,
+
+./configure --help
+
+
+Generally, developers wants to compile with debugging, in this case,
+give,
+
+./configure --enable-debug
+
+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.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..f0436ab
--- /dev/null
@@ -0,0 +1,21 @@
+#
+#  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 = lib silcd silc doc includes
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..1953d94
--- /dev/null
+++ b/README
@@ -0,0 +1,131 @@
+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.]
+
+
+Description
+===========
+
+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.
+
+
+Features
+========
+
+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.]
+
+ o Normal conferencing services such as private messages, channels,
+   channel messages, etc.  All traffic is secured and authenticated.
+
+ o 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.
+
+ o 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.
+
+ o 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.
+
+ o Supports data compression with GZIP to improve performance.
+
+ 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
+   the software.
+
+ o SILC client can be installed and used without root privileges.
+
+ o SILC client can be configured by system wide configuration files but
+   with user specific configuration files as well.
+
+
+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.
+
+
+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.]
+
+                               Pekka Riikonen <priikone@poseidon.pspt.fi>
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..ece615c
--- /dev/null
+++ b/TODO
@@ -0,0 +1,369 @@
+TODO
+====
+
+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.
+
+Feel free to contribute if you have the ability and free time - all the
+help is really appreciated - and needed.
+
+                                                       - Pekka
+
+[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.]
+
+
+New features TODO
+=================
+
+ o Extended SIM (SILC Module) support.  Currently only SILC Cipher API
+   and SILC Hash API may be used as SIM's.  What I have in mind is to
+   have extended support for SIM's so that basically any SILC API could
+   be used as SIM's.  This would open tremendous possiblities but
+   opens also issues on security that needs to be dealt with.
+
+   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...
+
+   This maybe post 1.0 task - dunno.
+
+ 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.
+
+ o More platform supports should be added.  The code is pretty much
+   generic but there are some parts that require porting (SIM).  Also, 
+   some support for different platforms is needed into configure.in.
+
+ o SILC requires currently GCC to work because we use GCC specific 
+   compilation options.  Generally any compiler that supports inline
+   functions and can build shared libraries (for SIMs) should work.  
+   These cases should be included into configure.in.
+
+
+TODO In SILC Client
+===================
+
+ 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 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 Finish WHOIS, finish JOIN and other commands that are partly
+   implemented.
+
+ o Input line on UI is buggy.  Cursor movement etc bugs.  Too lazy to
+   fix it.
+
+ o Logic for handling multiple same nicknames for example in private
+   message sending.  I guess the logic is done in server side but is
+   missing from client.
+
+ o Private message key setting is missing and must be implemented.
+   Currently private messages are encrypted with session keys.  This
+   is required by the protocol.
+
+ o Channel private key setting is missing and must be implemented.
+   Currently there cannot be private keys for channels.  Normal channel
+   keys (generated by server) are used.  This is required by the protocol.
+
+ o Public and private key generation is now done everytime the program
+   is run.  Now, this is only for testing period as I've been lazy to
+   do it any better for now.  This must be fixed.
+
+ o I guess, public key authentication (when connecting to a server)
+   is not working currently.  It is just matter of loading the keys
+   from file and using them (see corresponding code in server, it should
+   support public key authentication already).
+
+ o Multiple windows support.  Basic support for multiple windows already
+   exists but a lot is still missing to get it working.  Also, some
+   of the existing stuff probably needs to be tweaked a bit before the
+   multiple windows support could be done.  And of course the actual
+   commands that control the windows needs to be written (/WINDDOW).
+
+ o Implement /KEYMAP (or similiar) command to remap control and function
+   keys.
+
+ o Implement /ALIAS command to make command aliases.
+
+ o Implement /set/if/do/while etc as in IRC2.  Maybe post 1.0 task.
+   Some scripting would be good.
+
+ o Connection Authentication request resolving is missing and must be
+   done.  This is required by the protocol.
+
+ o Key Exchange protocol's responder side is missing from client.  
+   Generally it is possible for the client to be responder so it should
+   be implemented (See corresponding code from server).  Error handling
+   in the KE protocol is also in pretty bad shape in client.
+
+ o Configuration file loading from global and from local dirs.  This
+   is currently missing and I guess the global is only used.  Old SILC
+   version (in 1997) had ~./silc directory that I guess should be done
+   now as well.  The code for handling those exists but not in current
+   source tree.
+
+ o Configuration file format - could be better.
+
+ o Write help files for commands.  Nice format for the help files should
+   be selected.  I'm open for ideas.
+
+ o All allocations and freeing needs to be checked for memory leaks.
+   Also, return values from various allocations and functions needs to
+   checked.
+
+
+TODO In SILC Server
+===================
+
+ o Implement all commands on server side.  A lot of commands are still yet
+   to be implemented.  Most of them are trivial but some will require some
+   planning.  Go see the command.c for unimplemented commands.
+
+ o DNS/IP lookup blocks the server.  This must be fixed.  Check the
+   resolver stuff (resolver(3), resolver(5)).  Either we have to do the
+   own resolver stuff (through scheduler, if possible without writing
+   too much own stuff) or use threads.
+
+ o Lenght of the packet processing timeouts needs to be checked whether
+   they are too short or too long.  I haven't really tested whether they
+   are suitable.  They should be tested on high load which I haven't done
+   at all yet.
+
+ o 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 Compression routines are missing.  The protocol supports packet
+   compression thus it must be implemented.  SILC Comp API must be
+   defined.  zlib package is already included into the lib dir (in CVS,
+   not in distribution), but it is not used yet, and it requires some
+   tweaking on the Makefiles (we want static lib not shared).
+
+ o Cipher API needs to be made more consistent.  Some parts of the
+   code generated with current Cipher API looks really bad.  Same
+   is with PKCS API, even worse actually.  They need to be made
+   cleaner.  Introducing silc_cipher_encrypt/decrypt/set_key etc.
+   functions (I actually don't understand why have I left these un-done).
+
+ o Scheduler should automatically allocate task queues if NULL pointers 
+   are passed to the silc_schedule_init.  Would make initialization 
+   cleaner.
+
+ o Packet processing routines in client and server are actually pretty
+   much generic and should be moved from the client/server to the library
+   as generic routines (silc_<client/server>_packet_decrypt_rest* etc).
+   This requires heavy changes to the client and server.
+
+ o Random Number Generator needs some tweaking.  Reading /dev/random may
+   block resulting slow initialization of RNG.  Some other things in the
+   RNG may block as well.  Also, I have some pending changes to the RNG 
+   that needs to be commited (from Schneier's Yarrow-160 paper).  They 
+   should make the RNG even better.
+
+ o Logging should be made more generic in a way that application can
+   set to where the logging is destined to.  Now, it is always destined
+   to stdout (or stderr) which is a bad thing for client.  Ie. some
+   sort of logging registration functions or similiar should be done
+   (silclog.[ch] in core).  The actual output of logs should be done
+   by callback function in the application not in lib.
+
+ o I don't like the ID cache system currenly implemented.  Ugly and
+   not so good.  Must be rewritten very soon.
+
+ o All allocations and freeing needs to be checked for memory leaks.
+
+ o There are also checks missing from allocations whether the allocation
+   returns valid memory or NULL.  These are missing in library as well
+   in client and server.  Either all checks has to be added or we will
+   have to make sure that silc_*alloc()s always return valid memory
+   and assert()s if the system's memory allocator (*alloc()) fails.
+
+ o silc_buffer_[un]format() needs to be made more stable as it may
+   crash the SILC if malformed data is sent as argument.  There are a
+   lot of places in client and server where we trust directly data coming
+   from network and try to unformat it.  The unformatting routine needs
+   to be able handle situations where data sent is malformed, by mistake
+   or intentionally.  This is important as it is easy to crash the SILC
+   now by just sending malformed data.  Also, in client and server we
+   must start checking the return value from silc_buffer_[un]format.
+
+
+Other Things TODO
+=================
+
+ o Write manuals for server.
+
+ o Write manuals for client.
+
+ o Write SILC Library Reference manual.  This would include all the SILC
+   API's with simple examples how the functions are to be used.  This is
+   pretty easy to create by taking all the functions plus their comments
+   from source/header files.  However, same sort of reference manual 
+   should be written for client and server as well.
+
+
+TODO 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 
+   package as it has X.509 certificate support (and ASN.1 as well).  
+   The code does not look very good to my eye but it has some potentials.
+   This should be looked at more closely.
+
+   Naturally own SILC Certificate API has to be defined regardles what
+   the actual X.509 library is (OpenSSL X.509 or something else).  Other
+   choice is to write own X.509 library but I'm not going to do it - 
+   I can help to migrate the OpenSSL X.509 into SILC and I can help if 
+   someone would like to write the X.509 library - but I'm not going 
+   to start writing one myself.  Anyhow, the OpenSSL X.509 lib should
+   be checked.
+
+ o SSH2 public keys support.  Maybe - not really needed but could be
+   nice as SSH is widely used all over the place.  SILC Protocol 
+   supports SSH2 public keys.
+
+ 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
new file mode 100644 (file)
index 0000000..65e1903
--- /dev/null
@@ -0,0 +1,16 @@
+/* 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/apps/silc/Makefile.am b/apps/silc/Makefile.am
new file mode 100644 (file)
index 0000000..1b358e6
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  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
+
+bin_PROGRAMS = silc
+
+silc_SOURCES = \
+       silc.c \
+       client.c \
+       command.c \
+       command_reply.c \
+       clientconfig.c \
+       clientutil.c \
+       protocol.c \
+       screen.c
+
+LDADD = -L. -L.. -L../lib -lsilc -lcurses
+
+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
diff --git a/apps/silc/client.c b/apps/silc/client.c
new file mode 100644 (file)
index 0000000..80e2784
--- /dev/null
@@ -0,0 +1,2371 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#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
new file mode 100644 (file)
index 0000000..661bd83
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+
+  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/clientconfig.c b/apps/silc/clientconfig.c
new file mode 100644 (file)
index 0000000..7cdd815
--- /dev/null
@@ -0,0 +1,754 @@
+/*
+
+  serverconfig.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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "clientincludes.h"
+#include "clientconfig.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, 2 },
+  { "[hash]", 
+    SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
+  { "[connection]", 
+    SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
+  { "[commands]", 
+    SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND, 0 },
+  
+  { 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;
+
+  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 */
+  config_parse = NULL;
+  buffer = NULL;
+  silc_config_open(filename, &buffer);
+  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;
+  unsigned 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 (!strcmp(cp, 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
+       */
+
+      /* 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 && 
+       pc->section->type != SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND) {
+      /* 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 block length */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: Cipher block length not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      config->cipher->block_len = atoi(tmp);
+      silc_free(tmp);
+
+      /* Get key length */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: Cipher key length not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      config->cipher->key_len = atoi(tmp);
+      silc_free(tmp);
+
+      check = TRUE;
+      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;
+      }
+
+      /* 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;
+
+    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_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_PROTOCOL_CONN_AUTH_PASSWORD;
+
+       if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
+         config->conns->auth_meth = SILC_PROTOCOL_CONN_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_COMMAND:
+
+      if (!config->commands) {
+       config->commands = silc_calloc(1, sizeof(*config->commands));
+       config->commands->next = NULL;
+       config->commands->prev = NULL;
+      } else {
+       if (!config->commands->next) {
+         config->commands->next = silc_calloc(1, sizeof(*config->commands));
+         config->commands->next->next = NULL;
+         config->commands->next->prev = config->commands;
+         config->commands = config->commands->next;
+       }
+      }
+      
+      /* 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);
+      if (ret < 0)
+       break;
+
+      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->conns && config->conns->prev)
+    config->conns = config->conns->prev;
+  while (config->commands && config->commands->prev)
+    config->commands = config->commands->prev;
+  
+  SILC_LOG_DEBUG(("Done"));
+  
+  return TRUE;
+}
+
+/* Registers configured ciphers. These can then be allocated by the
+   client when needed. */
+
+void silc_client_config_register_ciphers(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg;
+  SilcClient client = (SilcClient)config->client;
+
+  SILC_LOG_DEBUG(("Registering configured ciphers"));
+
+  alg = config->cipher;
+  while(alg) {
+
+    if (!alg->sim_name) {
+      /* Crypto module is supposed to be built in. Nothing to be done
+        here except to test that the cipher really is built in. */
+      SilcCipher tmp = NULL;
+
+      if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
+       SILC_LOG_ERROR(("Unsupported 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;
+
+      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;
+
+      if ((silc_sim_load(sim))) {
+       cipher.set_key = 
+         silc_sim_getsym(sim, silc_sim_symname(alg->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_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_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_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_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++;
+      } else {
+       SILC_LOG_ERROR(("Error configuring ciphers"));
+       silc_client_stop(client);
+       exit(1);
+      }
+
+      /* Register the cipher */
+      silc_cipher_register(&cipher);
+#endif
+    }
+
+    alg = alg->next;
+  }
+}
+
+/* Registers configured PKCS's. */
+/* XXX: This really doesn't do anything now since we have statically
+   registered our PKCS's. This should be implemented when PKCS works
+   as SIM's. This checks now only that the PKCS user requested is 
+   really out there. */
+
+void silc_client_config_register_pkcs(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg = config->pkcs;
+  SilcClient client = (SilcClient)config->client;
+  SilcPKCS tmp = NULL;
+
+  SILC_LOG_DEBUG(("Registering configured PKCS"));
+
+  while(alg) {
+
+    if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
+      SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
+      silc_client_stop(client);
+      exit(1);
+    }
+    silc_free(tmp);
+
+    alg = alg->next;
+  }
+}
+
+/* Registers configured hash functions. These can then be allocated by the
+   client when needed. */
+
+void silc_client_config_register_hashfuncs(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg;
+  SilcClient client = (SilcClient)config->client;
+
+  SILC_LOG_DEBUG(("Registering configured hash functions"));
+
+  alg = config->hash_func;
+  while(alg) {
+
+    if (!alg->sim_name) {
+      /* Hash module is supposed to be built in. Nothing to be done
+        here except to test that the hash function really is built in. */
+      SilcHash tmp = NULL;
+
+      if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
+       SILC_LOG_ERROR(("Unsupported 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 */
+      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 */
+       client->sim = silc_realloc(client->sim,
+                                  sizeof(*client->sim) * 
+                                  (client->sim_count + 1));
+       client->sim[client->sim_count] = sim;
+       client->sim_count++;
+      } else {
+       SILC_LOG_ERROR(("Error configuring hash functions"));
+       silc_client_stop(client);
+       exit(1);
+      }
+
+      /* Register the cipher */
+      silc_hash_register(&hash);
+#endif
+    }
+
+    alg = alg->next;
+  }
+}
+
+
+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/silc/clientconfig.h b/apps/silc/clientconfig.h
new file mode 100644 (file)
index 0000000..f2c7a4a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+
+  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;
+  unsigned int block_len;
+  unsigned int 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;
+  unsigned short port;
+  struct SilcClientConfigSectionConnectionStruct *next;
+  struct SilcClientConfigSectionConnectionStruct *prev;
+#define SILC_CLIENT_CONFIG_AUTH_METH_PASSWD "passwd"
+#define SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY "pubkey"
+} SilcClientConfigSectionConnection;
+
+/* Holds all given commands from config file */
+typedef struct SilcClientConfigSectionCommandStruct {
+  char *command;
+  struct SilcClientConfigSectionCommandStruct *next;
+  struct SilcClientConfigSectionCommandStruct *prev;
+} SilcClientConfigSectionCommand;
+
+/* 
+   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;
+  SilcClientConfigSectionConnection *conns;
+  SilcClientConfigSectionCommand *commands;
+} 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_CONNECTION,
+  SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND = 253, /* Special section */
+} SilcClientConfigSectionType;
+
+/* SILC Configuration Section structure. */
+typedef struct {
+  const char *section;
+  SilcClientConfigSectionType type;
+  unsigned 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;
+  unsigned 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(unsigned int 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);
+SilcClientConfigSectionConnection *
+silc_client_config_find_connection(SilcClientConfig config, 
+                                  char *host, int port);
+
+#endif
diff --git a/apps/silc/clientutil.c b/apps/silc/clientutil.c
new file mode 100644 (file)
index 0000000..8946b28
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "clientincludes.h"
+
+/* Internal 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)
+{
+  int str_len, len;
+
+  str_len = strlen(message);
+  if (str_len > COLS - 1) {
+    /* Split overlapping words to next line */
+    /* XXX In principal this is wrong as this modifies the original
+       string as it replaces the last ' ' with '\n'. This could be done
+       with little more work so that it would not replace anything. */
+    len = COLS - 1;
+    while (1) {
+
+      while (len && message[len] != ' ')
+       len--;
+
+      if (!len)
+       break;
+
+      message[len] = '\n';
+      len += COLS - 1;
+      if (len > str_len)
+       break;
+    }
+  }
+
+  wprintw(win, "%s", 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];
+  
+  memset(message, 0, sizeof(message));
+  strncat(message, "\n ", 2);
+
+  va_start(vp, msg);
+  vsprintf(message + 1, msg, vp);
+  va_end(vp);
+  
+  /* Print the message */
+  silc_print_to_window(client->screen->output_win[0], message);
+}
+
+/* Returns user's mail path */
+
+char *silc_get_mail_path()
+{
+  char pathbuf[MAXPATHLEN];
+  char *path;
+  
+  if ((path = (char *)getenv("MAIL")) != 0) {
+    strncpy(pathbuf, path, strlen(path));
+  } else {
+    strcpy(pathbuf, _PATH_MAILDIR);
+    strcat(pathbuf, "/");
+    strcat(pathbuf, silc_get_username());
+  }
+
+  return strdup(pathbuf);
+}
+
+/* gets the number of the user's mails, if possible */
+
+int silc_get_number_of_emails()
+{
+  FILE *tl;
+  int num = 0;
+  char *filename;
+  char data[1024];
+  
+  filename = silc_get_mail_path();
+  
+  tl = fopen(filename, "r");
+  if (!tl) {
+    fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
+  } else {
+    while((fscanf(tl, "%s", data)) != EOF) { 
+      if(!strcmp(data, "Subject:"))
+       num++;
+    }
+    
+    fclose(tl);
+  }
+  
+  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. */
+
+int silc_client_time_til_next_min()
+{
+  time_t curtime;
+  struct tm *min;
+  
+  curtime = time(0);
+  min = localtime(&curtime);
+  
+  return 60 - min->tm_sec;
+}
+
+/* Asks passphrase from user on the input line. */
+
+char *silc_client_ask_passphrase(SilcClient client)
+{
+  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--;
+  }
+
+  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);
+
+  return ret;
+}
+
+/* Lists supported (builtin) ciphers */
+
+void silc_client_list_ciphers()
+{
+
+}
+
+/* Lists supported (builtin) hash functions */
+
+void silc_client_list_hash_funcs()
+{
+
+}
+
+/* Lists supported PKCS algorithms */
+
+void silc_client_list_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));
+    exit(1);
+  }
+
+  memset(input, 0, sizeof(input));
+
+  printf("%s", prompt);
+  fflush(stdout);
+
+  if ((read(fd, input, sizeof(input))) < 0) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    exit(1);
+  }
+
+  if (strlen(input) <= 1)
+    return NULL;
+
+  if (strchr(input, '\n'))
+    *strchr(input, '\n') = '\0';
+
+  return strdup(input);
+}
+
+/* Displays prompt on command line and takes passphrase with echo 
+   off from user. */
+
+char *silc_client_get_passphrase(const char *prompt)
+{
+#if 0
+  char input[2048];
+  char *ret;
+  int fd;
+  struct termios to;
+  struct termios to_old;
+
+  fd = open("/dev/tty", O_RDONLY);
+  if (fd < 0) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    exit(1);
+  }
+
+  signal(SIGINT, SIG_IGN);
+
+  /* Get terminal info */
+  tcgetattr(fd, &to);
+  to_old = to;
+
+  /* Echo OFF */
+  to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+  tcsetattr(fd, TCSANOW, &to);
+
+  memset(input, 0, sizeof(input));
+
+  printf("%s", prompt);
+  fflush(stdout);
+
+  if ((read(fd, input, sizeof(input))) < 0) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    exit(1);
+  }
+
+  if (strlen(input) <= 1) {
+    tcsetattr(fd, TCSANOW, &to_old);
+    return NULL;
+  }
+
+  if (strchr(input, '\n'))
+    *strchr(input, '\n') = '\0';
+
+  /* Restore old terminfo */
+  tcsetattr(fd, TCSANOW, &to_old);
+  signal(SIGINT, SIG_DFL);
+
+  ret = silc_calloc(strlen(input), sizeof(char));
+  memcpy(ret, input, strlen(input));
+  memset(input, 0, sizeof(input));
+  return ret;
+#else
+  return NULL;
+#endif
+}
+
+/* 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)
+{
+  SilcPKCS pkcs;
+  SilcRng rng;
+  unsigned char *key;
+  unsigned int key_len;
+  char *pkfile = NULL, *prvfile = NULL;
+
+  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 (!bits) {
+    char *length = NULL;
+    length = 
+      silc_client_get_input("Key length in bits [1024]: ");
+    if (!length)
+      bits = 1024;
+    else
+      bits = atoi(length);
+  }
+
+  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;
+  }
+
+ again_prv:
+  prvfile = silc_client_get_input("Private key filename: ");
+  if (!prvfile) {
+    printf("Private key filename must be defined\n");
+    goto again_prv;
+  }
+
+  /* Generate keys */
+  silc_pkcs_alloc(pkcs_name, &pkcs);
+  pkcs->pkcs->init(pkcs->context, bits, rng);
+
+  /* Save keys into file */
+  key = silc_pkcs_get_public_key(pkcs, &key_len);
+  silc_pkcs_save_public_key(pkcs, pkfile, key, key_len);
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+  key = silc_pkcs_get_private_key(pkcs, &key_len);
+  silc_pkcs_save_private_key(pkcs, prvfile, key, key_len, "");
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  silc_math_primegen_uninit();
+  silc_rng_free(rng);
+  silc_pkcs_free(pkcs);
+}
diff --git a/apps/silc/clientutil.h b/apps/silc/clientutil.h
new file mode 100644 (file)
index 0000000..e18cf2d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+
+  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 CLIENTUTIL_H
+#define CLIENTUTIL_H
+
+/* Prototypes */
+void silc_say(SilcClient client, char *msg, ...);
+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);
+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);
+
+#endif
diff --git a/apps/silc/command.c b/apps/silc/command.c
new file mode 100644 (file)
index 0000000..5c64ca8
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "clientincludes.h"
+
+/* Client command list. */
+SilcClientCommand silc_command_list[] =
+{
+  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
+  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
+  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
+                 SILC_CF_LAG | SILC_CF_REG, 3),
+  SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
+  SILC_CLIENT_CMD(kill, KILL, "KILL", 
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(oper, OPER, "OPER",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(restart, RESTART, "RESTART",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(die, DIE, "DIE",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
+  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
+  SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
+
+  /*
+   * Local. client specific commands
+   */
+  SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
+  SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
+  SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
+  SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
+  SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
+  SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
+
+  { NULL, 0, NULL, 0},
+};
+
+/* List of pending commands. */
+SilcClientCommandPending *silc_command_pending = NULL;
+
+/* Add new pending command to the list of pending commands. Currently
+   pending commands are executed from command replies, thus we can
+   execute any command after receiving some specific command reply.
+
+   The argument `reply_cmd' is the command reply from where the callback
+   function is to be called, thus, it IS NOT the command to be executed.
+
+   XXX: If needed in the future this support may be extended for
+   commands as well, when any command could be executed after executing
+   some specific command. */
+
+void silc_client_command_pending(SilcCommand reply_cmd,
+                                SilcClientCommandCallback callback,
+                                void *context)
+{
+  SilcClientCommandPending *reply, *r;
+
+  reply = silc_calloc(1, sizeof(*reply));
+  reply->reply_cmd = reply_cmd;
+  reply->context = context;
+  reply->callback = callback;
+
+  if (silc_command_pending == NULL) {
+    silc_command_pending = reply;
+    return;
+  }
+
+  for (r = silc_command_pending; r; r = r->next) {
+    if (r->next == NULL) {
+      r->next = reply;
+      break;
+    }
+  }
+}
+
+/* Deletes pending command by reply command type. */
+
+void silc_client_command_pending_del(SilcCommand reply_cmd)
+{
+  SilcClientCommandPending *r, *tmp;
+  
+  if (silc_command_pending) {
+    if (silc_command_pending->reply_cmd == reply_cmd) {
+      silc_free(silc_command_pending);
+      silc_command_pending = NULL;
+      return;
+    }
+
+    for (r = silc_command_pending; r; r = r->next) {
+      if (r->next && r->next->reply_cmd == reply_cmd) {
+       tmp = r->next;
+       r->next = r->next->next;
+       silc_free(tmp);
+       break;
+      }
+    }
+  }
+}
+
+/* Free command context and its internals */
+
+static void silc_client_command_free(SilcClientCommandContext cmd)
+{
+  int i;
+
+  if (cmd) {
+    for (i = 0; i < cmd->argc; i++)
+      silc_free(cmd->argv[i]);
+    silc_free(cmd);
+  }
+}
+
+/* Command WHOIS. This command is used to query information about 
+   specific user. */
+
+SILC_CLIENT_CMD_FUNC(whois)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcBuffer buffer;
+
+  if (cmd->argc < 2 || cmd->argc > 3) {
+    silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
+    goto out;
+  }
+
+  if (!cmd->client->current_win->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types);
+  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+SILC_CLIENT_CMD_FUNC(whowas)
+{
+}
+
+/* Command IDENTIFY. This command is used to query information about 
+   specific user, especially ID's. */
+
+SILC_CLIENT_CMD_FUNC(identify)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcBuffer buffer;
+
+  if (cmd->argc < 2 || cmd->argc > 3) {
+    silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
+    goto out;
+  }
+
+  if (!cmd->client->current_win->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types);
+  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command NICK. Shows current nickname/sets new nickname on current
+   window. */
+
+SILC_CLIENT_CMD_FUNC(nick)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientWindow win = NULL;
+  SilcBuffer buffer;
+
+  if (!cmd->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  /* Show current nickname */
+  if (cmd->argc < 2) {
+    if (cmd->sock) {
+      silc_say(cmd->client, "Your nickname is %s on server %s", 
+              win->nickname, win->remote_host);
+    } else {
+      silc_say(cmd->client, "Your nickname is %s", win->nickname);
+    }
+    goto out;
+  }
+
+  win = (SilcClientWindow)cmd->sock->user_data;
+
+  /* Set new nickname */
+  buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types);
+  silc_client_packet_send(cmd->client, cmd->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+  if (win->nickname)
+    silc_free(win->nickname);
+  win->nickname = strdup(cmd->argv[1]);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command SERVER. Connects to remote SILC server. This is local command. */
+
+SILC_CLIENT_CMD_FUNC(server)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  int len, port;
+  char *hostname;
+
+  if (cmd->argc < 2) {
+    /* Show current servers */
+    if (!cmd->client->current_win->sock) {
+      silc_say(cmd->client, "You are not connected to any server");
+      silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
+      goto out;
+    }
+
+    goto out;
+  }
+
+  /* See if port is included and then extract it */
+  if (strchr(cmd->argv[1], ':')) {
+    len = strcspn(cmd->argv[1], ":");
+    hostname = silc_calloc(len + 1, sizeof(char));
+    memcpy(hostname, cmd->argv[1], len);
+    port = atoi(cmd->argv[1] + 1 + len);
+  } else {
+    hostname = cmd->argv[1];
+    /* XXX */
+    port = 334;
+  }
+
+  /* Connect asynchronously to not to block user interface */
+  silc_client_connect_to_server(cmd->client, port, hostname);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+SILC_CLIENT_CMD_FUNC(list)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(topic)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(invite)
+{
+}
+
+/* Command QUIT. Closes connection with current server. */
+SILC_CLIENT_CMD_FUNC(quit)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcBuffer buffer;
+
+  if (!cmd->client->current_win->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
+                                      ++cmd->argv, ++cmd->argv_lens,
+                                      ++cmd->argv_types);
+  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+  /* Close connection */
+  silc_client_close_connection(cmd->client, cmd->sock);
+  cmd->client->screen->bottom_line->connection = NULL;
+  silc_screen_print_bottom_line(cmd->client->screen, 0);
+
+  silc_client_command_free(cmd);
+}
+
+SILC_CLIENT_CMD_FUNC(kill)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(info)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(connect)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(ping)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(oper)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(trace)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(notice)
+{
+}
+
+/* Command JOIN. Joins to a channel. */
+
+SILC_CLIENT_CMD_FUNC(join)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientWindow win = NULL;
+  SilcIDCache *id_cache = NULL;
+  SilcBuffer buffer;
+
+#define CIDC(x) win->channel_id_cache[(x) - 32]
+#define CIDCC(x) win->channel_id_cache_count[(x) - 32]
+
+  if (cmd->argc < 2) {
+    /* Show channels currently joined to */
+    if (!cmd->client->current_win->sock) {
+      silc_say(cmd->client, "No current channel for this window");
+      silc_say(cmd->client, 
+              "You are not connected to a server, use /SERVER to connect");
+      goto out;
+
+    }
+
+    goto out;
+  }
+
+  if (!cmd->client->current_win->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  win = (SilcClientWindow)cmd->sock->user_data;
+
+  /* See if we have joined to the requested channel already */
+  silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]), 
+                           cmd->argv[1], &id_cache);
+
+  if (id_cache) {
+    silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
+    win->current_channel = (SilcChannelEntry)id_cache->context;
+    cmd->client->screen->bottom_line->channel = cmd->argv[1];
+    silc_screen_print_bottom_line(cmd->client->screen, 0);
+    goto out;
+  }
+
+  /* Send JOIN command to the server */
+  buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types);
+  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+ out:
+  silc_client_command_free(cmd);
+#undef CIDC
+#undef CIDCC
+}
+
+SILC_CLIENT_CMD_FUNC(motd)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(umode)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(cmode)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(kick)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(restart)
+{
+}
+SILC_CLIENT_CMD_FUNC(close)
+{
+}
+SILC_CLIENT_CMD_FUNC(die)
+{
+}
+SILC_CLIENT_CMD_FUNC(silcoper)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(leave)
+{
+}
+
+SILC_CLIENT_CMD_FUNC(names)
+{
+}
+
+/*
+ * Local commands
+ */
+
+/* HELP command. This is local command and shows help on SILC */
+
+SILC_CLIENT_CMD_FUNC(help)
+{
+
+}
+
+/* CLEAR command. This is local command and clears current output window */
+
+SILC_CLIENT_CMD_FUNC(clear)
+{
+  SilcClient client = (SilcClient)context;
+
+  assert(client->current_win != NULL);
+  wclear((WINDOW *)client->current_win->screen);
+  wrefresh((WINDOW *)client->current_win->screen);
+}
+
+/* VERSION command. This is local command and shows version of the client */
+
+SILC_CLIENT_CMD_FUNC(version)
+{
+
+}
+
+/* Command MSG. Sends private message to user or list of users. */
+/* XXX supports only one destination */
+
+SILC_CLIENT_CMD_FUNC(msg)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientWindow win = NULL;
+  SilcClient client = cmd->client;
+  SilcBuffer buffer;
+  SilcIDCache *id_cache;
+  unsigned int nick_len;
+
+  if (cmd->argc < 3) {
+    silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
+    goto out;
+  }
+
+  if (!cmd->client->current_win->sock) {
+    silc_say(cmd->client, 
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  win = (SilcClientWindow)cmd->sock->user_data;
+
+#define CIDC(x) win->client_id_cache[(x) - 32], \
+                win->client_id_cache_count[(x) - 32]
+
+  /* Find ID from cache */
+  if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], 
+                               &id_cache) == FALSE) {
+    SilcClientCommandContext ctx;
+    char ident[512];
+
+    SILC_LOG_DEBUG(("Requesting Client ID from server"));
+
+    /* No ID found. Do query from the server. The query is done by 
+       sending simple IDENTIFY command to the server. */
+    ctx = silc_calloc(1, sizeof(*ctx));
+    ctx->client = client;
+    ctx->sock = cmd->sock;
+    memset(ident, 0, sizeof(ident));
+    snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
+    silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
+                                  &ctx->argv_types, &ctx->argc, 2);
+    silc_client_command_identify(ctx);
+
+    /* Mark this command to be pending command and to be executed after
+       we have received the IDENTIFY reply from server. */
+    silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
+                               silc_client_command_msg, context);
+    return;
+  }
+
+  /* Display the message for our eyes. */
+  silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
+
+  /* Send the private message */
+  silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
+                                         cmd->argv[2], cmd->argv_lens[2],
+                                         TRUE);
+ out:
+  silc_client_command_free(cmd);
+#undef CIDC
+}
+
+SILC_CLIENT_CMD_FUNC(away)
+{
+}
diff --git a/apps/silc/command.h b/apps/silc/command.h
new file mode 100644 (file)
index 0000000..19db809
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+
+  command.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+/* 
+   Structure holding one command and pointer to its function. 
+
+   SilcCommandCb cb
+
+       Callback function called when this command is executed.
+
+   SilcCommand cmd
+
+       The actual command. These are defined in silccore/silccommand.h
+
+   char *name
+
+       Logical name of the command. This is the visible command name
+       that user uses when calling command. Eg. NICK.
+
+   SilcCommandFlag flags
+
+       Flags for the command. These set how command behaves on different
+       situations. Server sets these flags as well, but to be sure
+       that our client never sends wrong commands we preserve the
+       flags on client side as well.
+
+       XXX: We preserve these so that we define them but currently we
+       don't check the flags at all.
+
+*/
+typedef struct {
+  SilcCommandCb cb;
+  SilcCommand cmd;
+  char *name;
+  SilcCommandFlag flags;
+  unsigned int max_args;
+} SilcClientCommand;
+
+/* All client commands */
+extern SilcClientCommand silc_command_list[];
+
+/* Client command callback function. This included into Command Context, 
+   and if it is defined it will be executed when executing the command. */
+typedef void (*SilcClientCommandCallback)(void *context);
+
+/* Context sent as argument to all commands */
+typedef struct {
+  SilcClient client;
+  SilcSocketConnection sock;
+  unsigned int argc;
+  unsigned char **argv;
+  unsigned int *argv_lens;
+  unsigned int *argv_types;
+} *SilcClientCommandContext;
+
+/* Structure holding pending commands. If command is pending it will be
+   executed after command reply has been executed. */
+/* XXX This support may added for commands as well and not just command
+   replies, if needed later. */
+typedef struct SilcClientCommandPendingStruct {
+  SilcCommand reply_cmd;
+  void *context;
+  SilcClientCommandCallback callback;
+
+  struct SilcClientCommandPendingStruct *next;
+} SilcClientCommandPending;
+
+/* List of pending commands */
+extern SilcClientCommandPending *silc_command_pending;
+
+/* Macros */
+
+/* Macro used for command declaration in command list structure */
+#define SILC_CLIENT_CMD(func, cmd, name, flags, args) \
+{ silc_client_command_##func, SILC_COMMAND_##cmd, name, flags, args }
+
+/* Macro used to declare command functions */
+#define SILC_CLIENT_CMD_FUNC(func) \
+void silc_client_command_##func(void *context)
+
+/* Checks for pending commands */
+#define SILC_CLIENT_COMMAND_CHECK_PENDING(ctx)         \
+do {                                                   \
+  if (silc_command_pending) {                          \
+    SilcClientCommandPending *r;                       \
+    SilcCommand cmd;                                   \
+                                                       \
+    cmd = silc_command_get(payload);                   \
+    for (r = silc_command_pending; r; r = r->next) {   \
+      if (r->reply_cmd == cmd) {                       \
+       ctx->context = r->context;                      \
+       ctx->callback = r->callback;                    \
+       break;                                          \
+      }                                                        \
+    }                                                  \
+  }                                                    \
+} while(0)
+
+/* Executed pending command */
+#define SILC_CLIENT_COMMAND_EXEC_PENDING(ctx, cmd)     \
+do {                                                   \
+  if (ctx->callback) {                                 \
+    (*ctx->callback)(ctx->context);                    \
+    silc_client_command_pending_del((cmd));            \
+  }                                                    \
+} while(0)
+
+/* Prototypes */
+void silc_client_command_pending(SilcCommand reply_cmd,
+                                SilcClientCommandCallback callback,
+                                void *context);
+void silc_client_command_pending_del(SilcCommand reply_cmd);
+SILC_CLIENT_CMD_FUNC(whois);
+SILC_CLIENT_CMD_FUNC(whowas);
+SILC_CLIENT_CMD_FUNC(identify);
+SILC_CLIENT_CMD_FUNC(nick);
+SILC_CLIENT_CMD_FUNC(server);
+SILC_CLIENT_CMD_FUNC(list);
+SILC_CLIENT_CMD_FUNC(topic);
+SILC_CLIENT_CMD_FUNC(invite);
+SILC_CLIENT_CMD_FUNC(quit);
+SILC_CLIENT_CMD_FUNC(kill);
+SILC_CLIENT_CMD_FUNC(info);
+SILC_CLIENT_CMD_FUNC(connect);
+SILC_CLIENT_CMD_FUNC(ping);
+SILC_CLIENT_CMD_FUNC(oper);
+SILC_CLIENT_CMD_FUNC(join);
+SILC_CLIENT_CMD_FUNC(motd);
+SILC_CLIENT_CMD_FUNC(umode);
+SILC_CLIENT_CMD_FUNC(cmode);
+SILC_CLIENT_CMD_FUNC(kick);
+SILC_CLIENT_CMD_FUNC(restart);
+SILC_CLIENT_CMD_FUNC(close);
+SILC_CLIENT_CMD_FUNC(die);
+SILC_CLIENT_CMD_FUNC(silcoper);
+SILC_CLIENT_CMD_FUNC(leave);
+SILC_CLIENT_CMD_FUNC(names);
+SILC_CLIENT_CMD_FUNC(help);
+SILC_CLIENT_CMD_FUNC(clear);
+SILC_CLIENT_CMD_FUNC(version);
+SILC_CLIENT_CMD_FUNC(msg);
+SILC_CLIENT_CMD_FUNC(away);
+
+#endif
diff --git a/apps/silc/command_reply.c b/apps/silc/command_reply.c
new file mode 100644 (file)
index 0000000..3b54072
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#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/command_reply.h b/apps/silc/command_reply.h
new file mode 100644 (file)
index 0000000..36b3128
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+
+  command_reply.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef COMMAND_REPLY_H
+#define COMMAND_REPLY_H
+
+/* Structure holding one command reply and pointer to its function. */
+typedef struct {
+  SilcCommandCb cb;
+  SilcCommand cmd;
+} SilcClientCommandReply;
+
+/* All client command replys */
+extern SilcClientCommandReply silc_command_reply_list[];
+
+/* Context sent as argument to all command reply functions */
+typedef struct {
+  SilcClient client;
+  SilcSocketConnection sock;
+  SilcCommandPayload payload;
+
+  /* If defined this executes the pending command. */
+  void *context;
+  SilcClientCommandCallback callback;
+} *SilcClientCommandReplyContext;
+
+/* Macros */
+
+/* Macro used for command declaration in command reply list structure */
+#define SILC_CLIENT_CMD_REPLY(func, cmd ) \
+{ silc_client_command_reply_##func, SILC_COMMAND_##cmd }
+
+/* Macro used to declare command reply functions */
+#define SILC_CLIENT_CMD_REPLY_FUNC(func) \
+void silc_client_command_reply_##func(void *context)
+
+/* Macro used to execute command replies */
+#define SILC_CLIENT_COMMAND_REPLY_EXEC(ctx)            \
+do {                                                   \
+  SilcClientCommandReply *cmd;                         \
+                                                       \
+  for (cmd = silc_command_reply_list; cmd->cb; cmd++)  \
+    if (cmd->cmd == silc_command_get(ctx->payload)) {  \
+      cmd->cb(ctx);                                    \
+      break;                                           \
+    }                                                  \
+                                                       \
+  if (cmd == NULL) {                                   \
+    silc_free(ctx);                                    \
+    return;                                            \
+  }                                                    \
+} while(0)
+
+/* Prototypes */
+void silc_client_command_reply_process(SilcClient client,
+                                      SilcSocketConnection sock,
+                                      SilcBuffer buffer);
+SILC_CLIENT_CMD_REPLY_FUNC(whois);
+SILC_CLIENT_CMD_REPLY_FUNC(whowas);
+SILC_CLIENT_CMD_REPLY_FUNC(identify);
+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);
+SILC_CLIENT_CMD_REPLY_FUNC(oper);
+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(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);
+SILC_CLIENT_CMD_REPLY_FUNC(msg);
+
+#endif
diff --git a/apps/silc/idlist.h b/apps/silc/idlist.h
new file mode 100644 (file)
index 0000000..9c769e9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+
+  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/protocol.c b/apps/silc/protocol.c
new file mode 100644 (file)
index 0000000..bfbb2aa
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#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
new file mode 100644 (file)
index 0000000..1c40ef3
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+
+  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
new file mode 100644 (file)
index 0000000..7dc0bfd
Binary files /dev/null and b/apps/silc/pubkey.pub differ
diff --git a/apps/silc/screen.c b/apps/silc/screen.c
new file mode 100644 (file)
index 0000000..10a52f0
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+
+  screen.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.
+
+*/
+/*
+ * SILC client screen routines. These implement the user interface
+ * on ncurses routines. Most of these routines were taken from the
+ * old version of the SILC client dating back to 1997.
+ */
+/* XXX: Input line handling is really buggy! */
+/*
+ * $Id$
+ * $Log$
+ * Revision 1.1  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "clientincludes.h"
+
+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->insert = TRUE;
+
+  initscr();
+  cbreak();
+  nonl();
+  noecho();
+
+  silc_screen_create_output_window(new);
+  silc_screen_create_input_window(new);
+
+  return new;
+}
+
+/* Creates one (main) output window. Returns new created physical 
+   window. */
+
+WINDOW *silc_screen_create_output_window(SilcScreen screen)
+{
+  assert(screen != NULL);
+
+  screen->output_win = silc_malloc(sizeof(*screen->output_win) * 1);
+  screen->output_win_count = 1;
+  screen->output_win[0] = newwin(LINES - 3, COLS, 1, 0);
+  scrollok(screen->output_win[0], TRUE);
+  idlok(screen->output_win[0], TRUE);
+  wrefresh(screen->output_win[0]);
+
+  return screen->output_win[0];
+}
+
+/* Adds new output window. Return new created physical window. */
+
+WINDOW *silc_screen_add_output_window(SilcScreen screen)
+{
+  int i;
+
+  assert(screen != NULL);
+
+  screen->output_win = silc_realloc(screen->output_win, 
+                                   (screen->output_win_count + 1) *
+                                   sizeof(*screen->output_win));
+  i = screen->output_win_count;
+  screen->output_win[i] = newwin(LINES - 3, COLS, 1, 0);
+  scrollok(screen->output_win[i], TRUE);
+  idlok(screen->output_win[i], TRUE);
+  wrefresh(screen->output_win[i]);
+  screen->output_win_count++;
+
+  return screen->output_win[i];
+}
+
+void silc_screen_create_input_window(SilcScreen screen)
+{
+  assert(screen != NULL);
+
+  screen->input_win = newwin(0, COLS, LINES - 1, 0);
+  scrollok(screen->input_win, TRUE);
+  keypad(screen->input_win, TRUE);
+  wrefresh(screen->input_win);
+}
+
+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);
+  
+  /* Print empty line */
+  for (i = 0; i < COLS - 1; i++)
+    mvwprintw(screen->upper_stat_line, 0, i, " ");
+  
+  /* Print stuff with justify */
+  justify = COLS / 5;
+  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);
+  wrefresh(screen->upper_stat_line);
+}
+
+void silc_screen_init_output_status_line(SilcScreen screen)
+{
+  int i;
+
+  assert(screen != NULL);
+
+  screen->output_stat_line = silc_calloc(1, sizeof(*screen->output_stat_line));
+  
+  screen->output_stat_line[0] = newwin(1, COLS, LINES - 2, 0);
+  scrollok(screen->output_stat_line[0], FALSE);
+  wattrset(screen->output_stat_line[0], A_REVERSE);
+  
+  /* print first just blank line */
+  for (i = 0; i < COLS - 1; i++)
+    mvwprintw(screen->output_stat_line[0], 0, i, " ");
+
+  /* Allocate bottom line */
+  screen->bottom_line = silc_calloc(1, sizeof(*screen->bottom_line));
+
+  wattrset(screen->output_stat_line[0], A_NORMAL);
+  wrefresh(screen->output_stat_line[0]);
+}
+
+void silc_screen_print_clock(SilcScreen screen)
+{
+  time_t curtime;
+  struct tm *tp;
+
+  curtime = time(0);
+  tp = localtime(&curtime);
+
+  mvwprintw(screen->upper_stat_line, 0, COLS - 8, "[%02d:%02d] ", 
+           tp->tm_hour, tp->tm_min);
+  wrefresh(screen->upper_stat_line);
+}
+
+/* Prints current cursor coordinates on some output stat line */
+
+void silc_screen_print_coordinates(SilcScreen screen, int win_index)
+{
+  wattrset(screen->output_stat_line[win_index], A_REVERSE);
+  mvwprintw(screen->output_stat_line[win_index], 0, COLS - 10,
+           "[%4d,%3d]", screen->input_pos, LINES);
+  wrefresh(screen->output_stat_line[win_index]);
+  wattrset(screen->output_stat_line[win_index], A_NORMAL);
+}
+
+/* Prints bottom line (the status line) of the screen. */
+
+void silc_screen_print_bottom_line(SilcScreen screen, int win_index)
+{
+  char buf[512];
+  SilcScreenBottomLine line = screen->bottom_line;
+  int i, len;
+
+  memset(buf, 0, sizeof(buf));
+
+  if (line->mode) {
+    len = strlen(line->mode);
+    strncat(buf, line->mode, len);
+  }
+
+  if (line->nickname) {
+    len = strlen(line->nickname);
+    strncat(buf, line->nickname, len > SILC_SCREEN_MAX_NICK_LEN ? 
+           SILC_SCREEN_MAX_NICK_LEN : len);
+  }
+
+  if (line->connection) {
+    len = strlen(line->connection);
+    strncat(buf, " via ", 5);
+    strncat(buf, line->connection, len > SILC_SCREEN_MAX_CONN_LEN ? 
+           SILC_SCREEN_MAX_CONN_LEN : len);
+  }
+
+  if (line->channel) {
+    len = strlen(line->channel);
+    strncat(buf, " ", 1);
+    strncat(buf, line->channel, len > SILC_SCREEN_MAX_CHANNEL_LEN ?
+           SILC_SCREEN_MAX_CHANNEL_LEN : len);
+  }
+
+  wattrset(screen->output_stat_line[win_index], A_REVERSE);
+
+  for (i = 0; i < COLS - 10; i++)
+    mvwprintw(screen->output_stat_line[win_index], 0, i, " ");
+
+  mvwprintw(screen->output_stat_line[win_index], 0, 0, " %s", buf);
+  silc_screen_print_coordinates(screen, win_index);
+  wrefresh(screen->output_stat_line[win_index]);
+  wattrset(screen->output_stat_line[win_index], A_NORMAL);
+}
+
+/* Refresh all windows */
+
+void silc_screen_refresh_all(SilcScreen screen)
+{
+  int i;
+
+  assert(screen != NULL);
+
+  redrawwin(screen->upper_stat_line);
+
+  for (i = 0; i < screen->output_win_count; i++) {
+    wrefresh(screen->output_win[i]);
+    redrawwin(screen->output_win[i]);
+  }
+
+  wrefresh(screen->input_win);
+  redrawwin(screen->input_win);
+}
+
+/* Refreshes a window */
+
+void silc_screen_refresh_win(WINDOW *win)
+{
+  assert(win != NULL);
+
+  redrawwin(win);
+  wrefresh(win);
+}
+
+/* Resets input window */
+
+void silc_screen_input_reset(SilcScreen screen)
+{
+  int i;
+
+  assert(screen != NULL);
+  for (i = 0; i < COLS - 1; i++)
+    mvwprintw(screen->input_win, 0, i, " ");
+  mvwprintw(screen->input_win, 0, 0, "");
+  wrefresh(screen->input_win);
+  screen->input_pos = 0;
+  screen->input_end = 0;
+  screen->cursor_pos = 0;
+  screen->virtual_window = 0;
+}
+
+/* Backspace. Removes one character from input windows. */
+
+void silc_screen_input_backspace(SilcScreen screen)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  /* Return directly if at the start of input line */
+  if (screen->input_pos == 0)
+    return;
+
+  if (screen->virtual_window) {
+    if (screen->cursor_pos <= 10) {
+      int i;
+
+      /* Clear line */
+      for (i = 0; i < COLS; i++)
+        mvwprintw(win, 0, i, " ");
+      mvwprintw(win, 0, 0, "");
+
+      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;
+      wrefresh(win);
+    }
+  }
+
+  screen->cursor_pos--;
+  screen->input_pos--;
+  screen->input_end--;
+  mvwdelch(win, 0, screen->cursor_pos);
+
+  if (screen->input_pos < screen->input_end)
+    /* Delete from inside the input line */
+    SILC_SCREEN_INPUT_DELETE(buffer, screen->input_pos, screen->input_end);
+  else
+    /* Delete from the end of the input line */
+    buffer[screen->input_pos] = 0;
+
+  wrefresh(win);
+}
+
+/* Switches insert on input window on/off */
+
+void silc_screen_input_insert(SilcScreen screen)
+{
+  assert(screen != NULL);
+
+  screen->insert = screen->insert == TRUE ? FALSE : TRUE;
+}
+
+/* Moves cursor one character length to rightward */
+
+void silc_screen_input_cursor_right(SilcScreen screen)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  /* Return directly if we are at the end of input line */
+  if (screen->cursor_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
+    return;
+
+  /* Make sure cursor doesn't advance over the end of the line */
+  if (screen->input_pos >= screen->input_end)
+    return;
+
+  /* When cursor advances enough we switch to new window and show
+     rest of the typed characters on the screen. */
+  if (screen->cursor_pos >= (COLS - 5)) {
+    int i;
+
+    /* Clear line */
+    for (i = 0; i < COLS; i++)
+      mvwprintw(win, 0, i, " ");
+    mvwprintw(win, 0, 0, "");
+
+    waddnstr(win, &buffer[screen->input_pos - 10], 
+            ((screen->input_pos - 10) - screen->input_end >= COLS) ?
+            COLS : (screen->input_pos - 10) - screen->input_end);
+    screen->cursor_pos = 10;
+    wrefresh(win);
+
+    screen->virtual_window++;
+  }
+
+  screen->cursor_pos++;
+  screen->input_pos++;
+  wmove(win, 0, screen->cursor_pos);
+  wrefresh(win);
+}
+
+/* Moves cursor one character length to leftward */
+
+void silc_screen_input_cursor_left(SilcScreen screen)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  /* Return directly if at the start of input line */
+  if (screen->input_pos == 0)
+    return;
+
+  /* When cursor advances enough we switch to new window and show
+     rest of the typed characters on the screen. */
+  if (screen->virtual_window) {
+    if (screen->cursor_pos <= 10) {
+      int i;
+
+      /* Clear line */
+      for (i = 0; i < COLS; i++)
+        mvwprintw(win, 0, i, " ");
+      mvwprintw(win, 0, 0, "");
+
+      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;
+      wrefresh(win);
+    }
+  }
+
+  screen->cursor_pos--;
+  screen->input_pos--;
+  wmove(win, 0, screen->cursor_pos);
+  wrefresh(win);
+}
+
+/* Moves cursor at the very start of the input line */
+
+void silc_screen_input_cursor_home(SilcScreen screen)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  wclear(win);
+  waddnstr(win, &buffer[0], COLS);
+  wrefresh(win);
+
+  screen->input_pos = 0;
+  screen->cursor_pos = 0;
+  screen->virtual_window = 0;
+}
+
+/* Moves cursor at the very end of the input line */
+
+void silc_screen_input_cursor_end(SilcScreen screen)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  wclear(win);
+  waddnstr(win, &buffer[screen->input_end - 10], 10);
+  wrefresh(win);
+
+  screen->input_pos = screen->input_end;
+  screen->cursor_pos = 10;
+  /* XXX */
+  screen->virtual_window = 0;
+}
+
+/* Prints typed character into the input window for user to see. Character 
+   attributes must be set separately outside this function. */
+
+void silc_screen_input_print(SilcScreen screen, unsigned char c)
+{
+  WINDOW *win;
+  char *buffer;
+
+  assert(screen != NULL);
+  buffer = screen->input_buffer;
+  win = screen->input_win;
+
+  /* Return directly if input window is full */
+  if (screen->input_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
+    return;
+
+  /* The input window is COLS wide but one can type into it at most
+     SILC_SCREEN_INPUT_SIZE characters. When COLS - 5 characters is
+     typed the window is cleared and the cursor is moved at the tenth
+     character in the input window. Ten last typed character is then
+     showed at the start of the window. */
+  if (screen->cursor_pos >= (COLS - 5)) {
+    int i;
+
+    /* Clear line */
+    for (i = 0; i < COLS; i++)
+      mvwprintw(win, 0, i, " ");
+    mvwprintw(win, 0, 0, "");
+
+    /* Show ten last typed characters from the buffer on the screen */
+    waddnstr(win, &buffer[screen->input_pos - 10], 10);
+    screen->cursor_pos = 10;
+    wrefresh(win);
+
+    screen->virtual_window++;
+  }
+
+  if (screen->input_pos < screen->input_end) {
+    /* User moved cursor into the typed line. We are not adding 
+       character at the end of the line anymore */
+
+    if (screen->insert == FALSE) {
+      /* Add new character somewhere inside typed line. The input
+        line position is not advanced since a character was replaced
+        by the new character. */
+      waddch(win, c);
+      buffer[screen->input_pos] = c;
+      screen->cursor_pos++;
+      screen->input_pos++;
+      screen->input_end = screen->input_pos;
+    } else {
+      /* Insert new character somewhere inside typed line. Other
+        characters are moved forward. We must advance the input line
+        posititon. */
+      winsch(win, c);
+      wmove(win, 0, screen->cursor_pos + 1);
+      SILC_SCREEN_INPUT_INSERT(buffer, screen->input_pos, 
+                              c, screen->input_end);
+      screen->cursor_pos++;
+      screen->input_pos++;
+      screen->input_end++;
+    }
+  } else {
+    /* Add new character at the end of input line */
+    waddch(win, c);
+    buffer[screen->input_pos] = c;
+    screen->input_pos++;
+    screen->cursor_pos++;
+    screen->input_end = screen->input_pos;
+  }
+
+  /* Advance the cursor position. Cursor moves one to rightward always */
+  wrefresh(win);
+}
+
+/* Prints prompt to the input window. Cursors position aftern printing
+   is length of the prompt. */
+
+void silc_screen_input_print_prompt(SilcScreen screen, char *prompt)
+{
+  WINDOW *win;
+
+  assert(screen != NULL);
+  win = screen->input_win;
+
+  wclear(win);
+  waddnstr(win, prompt, strlen(prompt));
+  wrefresh(win);
+
+  screen->input_pos = strlen(prompt);
+  screen->cursor_pos = strlen(prompt);
+  screen->virtual_window = 0;
+}
diff --git a/apps/silc/screen.h b/apps/silc/screen.h
new file mode 100644 (file)
index 0000000..25d083b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+
+  screen.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 SCREEN_H
+#define SCREEN_H
+
+typedef struct {
+  char *mode;
+  char *nickname;
+  char *connection;
+  char *channel;
+} *SilcScreenBottomLine;
+
+typedef struct {
+  /* Status line window top of the screen */
+  WINDOW *upper_stat_line;
+
+  /* Output windows */
+  WINDOW **output_win;
+  WINDOW **output_stat_line;
+  unsigned int 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;
+  int virtual_window;
+
+  /* Bottom line on screen */
+  SilcScreenBottomLine bottom_line;
+
+  /* On/off flag for insert */
+  int insert;
+
+  /* XXX */
+  struct upper_status_line {
+    char *program_name;
+    char *program_version;
+  } u_stat_line;
+
+} SilcScreenObject;
+
+typedef SilcScreenObject *SilcScreen;
+
+/* Size of the input window. User can type this many characters into
+   the window. After that no more characters may be added into the 
+   window. */
+#define SILC_SCREEN_INPUT_WIN_SIZE 1024
+
+/* Maximum length of nickaname that will be shown on the screen */
+#define SILC_SCREEN_MAX_NICK_LEN 16
+
+/* Maximum length of channel name that will be shown on the screen */
+#define SILC_SCREEN_MAX_CHANNEL_LEN 20
+
+/* Maximum length of connection name that will be shown on the screen */
+#define SILC_SCREEN_MAX_CONN_LEN 20
+
+/* Macros */
+
+/* Macro used to insert typed character into the buffer. The character
+   is not added at the end of the buffer but somewhere in between. */
+#define SILC_SCREEN_INPUT_INSERT(__x, __y, __ch, __end)        \
+do {                                                   \
+  unsigned char __tmp[SILC_SCREEN_INPUT_WIN_SIZE + 1]; \
+  memcpy(__tmp, &(__x)[(__y)], (__end) - (__y));       \
+  (__x)[(__y)] = __ch;                                 \
+  memcpy(&(__x)[(__y) + 1], __tmp, (__end) - (__y));   \
+} while(0)
+
+/* Macro used to delete character from the buffer. The character
+   is not removed from the end of the buffer but somewhere in between. */
+#define SILC_SCREEN_INPUT_DELETE(__x, __y, __end)      \
+do {                                                   \
+  unsigned char __tmp[SILC_SCREEN_INPUT_WIN_SIZE + 1]; \
+  memcpy(__tmp, &(__x)[(__y) + 1], (__end));           \
+  memset(&(__x)[(__y)], 0, (__end) - (__y) + 1);       \
+  memcpy(&(__x)[(__y)], __tmp, strlen(__tmp));         \
+} while(0)
+
+/* Prototypes */
+SilcScreen silc_screen_init();
+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_init_output_status_line(SilcScreen screen);
+void silc_screen_print_clock(SilcScreen screen);
+void silc_screen_print_coordinates(SilcScreen screen, int win_index);
+void silc_screen_print_bottom_line(SilcScreen screen, int win_index);
+void silc_screen_refresh_all(SilcScreen screen);
+void silc_screen_refresh_win(WINDOW *win);
+void silc_screen_input_reset(SilcScreen screen);
+void silc_screen_input_backspace(SilcScreen screen);
+void silc_screen_input_insert(SilcScreen screen);
+void silc_screen_input_cursor_right(SilcScreen screen);
+void silc_screen_input_cursor_left(SilcScreen screen);
+void silc_screen_input_cursor_home(SilcScreen screen);
+void silc_screen_input_cursor_end(SilcScreen screen);
+void silc_screen_input_print(SilcScreen screen, unsigned char c);
+void silc_screen_input_print_prompt(SilcScreen screen, char *prompt);
+
+#endif
diff --git a/apps/silc/silc.c b/apps/silc/silc.c
new file mode 100644 (file)
index 0000000..fa4c189
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+
+  silc.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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "clientincludes.h"
+#include "version.h"
+
+/* Long command line options */
+static struct option long_opts[] = 
+{
+  /* Generic options */
+  { "server", 1, NULL, 's' },
+  { "port", 1, NULL, 'p' },
+  { "nickname", 1, NULL, 'n' },
+  { "channel", 1, NULL, 'c' },
+  { "cipher", 1, NULL, 'r' },
+  { "public-key", 1, NULL, 'b' },
+  { "private-key", 1, NULL, 'k' },
+  { "config-file", 1, NULL, 'f' },
+  { "no-silcrc", 0, NULL, 'q' },
+  { "help", 0, NULL, 'h' },
+  { "version", 0, NULL, 'V' },
+  { "list-ciphers", 0, NULL, 1 },
+  { "list-hash-funcs", 0, NULL, 2 },
+  { "list-pkcs", 0, NULL, 3 },
+
+  /* Key management options */
+  { "create-key-pair", 0, NULL, 'C' },
+  { "pkcs", 1, NULL, 10 },
+  { "bits", 1, NULL, 11 },
+
+  { NULL, 0, NULL, 0 }
+};
+
+/* Command line option variables */
+static char *opt_server = NULL;
+static int opt_port = 0;
+static char *opt_nickname = NULL;
+static char *opt_channel = NULL;
+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 int opt_create_keypair = FALSE;
+static char *opt_pkcs = NULL;
+static int opt_bits = 0;
+
+/* Prints out the usage of silc client */
+
+void usage()
+{
+  printf("\
+Usage: silc [options]\n\
+\n\
+  Generic Options:\n\
+  -s, --server=HOST            Open connection to server HOST\n\
+  -p, --port=PORT              Set PORT as default port to connect\n\
+  -n, --nickname=STRING        Set default nickname on startup\n\
+  -c, --channel=STRING         Join channel on startup\n\
+  -r, --cipher=CIPHER          Use CIPHER as default cipher in SILC\n\
+  -b, --public-key=FILE        Public key used in SILC\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\
+  -h, --help                   Display this help message\n\
+  -V, --version                Display version\n\
+      --list-ciphers           List supported ciphers\n\
+      --list-hash-funcs        List supported hash functions\n\
+      --list-pkcs              List supported PKCS's\n\
+\n\
+  Key Management 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\
+\n");
+}
+
+int main(int argc, char **argv)
+{
+  int opt, option_index = 1;
+  int ret;
+  SilcClient silc = NULL;
+  SilcClientConfig config = NULL;
+  
+  if (argc > 1) 
+    {
+      while ((opt = 
+             getopt_long(argc, argv,
+                         "s:p:n:c:b:k:f:qhVC",
+                         long_opts, &option_index)) != EOF)
+       {
+         switch(opt) 
+           {
+             /* 
+              * Generic options
+              */
+           case 's':
+             if (optarg)
+               opt_server = strdup(optarg);
+             break;
+           case 'p':
+             if (optarg)
+               opt_port = atoi(optarg);
+             break;
+           case 'n':
+             if (optarg)
+               opt_nickname = strdup(optarg);
+             break;
+           case 'c':
+             if (optarg)
+               opt_channel = strdup(optarg);
+             break;
+           case 'r':
+             if (optarg)
+               opt_cipher = strdup(optarg);
+             break;
+           case 'b':
+             if (optarg)
+               opt_public_key = strdup(optarg);
+             break;
+           case 'k':
+             if (optarg)
+               opt_private_key = strdup(optarg);
+             break;
+           case 'f':
+             if (optarg)
+               opt_config_file = strdup(optarg);
+             break;
+           case 'q':
+             opt_no_silcrc = TRUE;
+             break;
+           case 'h':
+             usage();
+             exit(0);
+             break;
+           case 'V':
+             printf("\
+SILC Secure Internet Live Conferencing, version %s\n", 
+                    silc_version);
+             printf("\
+(c) 1997 - 2000 Pekka Riikonen <priikone@poseidon.pspt.fi>\n");
+             exit(0);
+             break;
+           case 1:
+             silc_client_list_ciphers();
+             exit(0);
+             break;
+           case 2:
+             silc_client_list_hash_funcs();
+             exit(0);
+             break;
+           case 3:
+             silc_client_list_pkcs();
+             exit(0);
+             break;
+
+             /*
+              * Key management options
+              */
+           case 'C':
+             opt_create_keypair = TRUE;
+             break;
+           case 10:
+             if (optarg)
+               opt_pkcs = strdup(optarg);
+             break;
+           case 11:
+             if (optarg)
+               opt_bits = atoi(optarg);
+             break;
+
+           default:
+             exit(0);
+             break;
+           }
+       }
+    }
+
+  /* Init signals */
+  signal(SIGHUP, SIG_DFL);
+  signal(SIGTERM, SIG_DFL);
+  signal(SIGPIPE, SIG_IGN);
+  signal(SIGCHLD, SIG_DFL);
+  signal(SIGALRM, SIG_IGN);
+  signal(SIGQUIT, SIG_IGN);
+  signal(SIGSEGV, SIG_DFL);
+  signal(SIGBUS, SIG_DFL);
+  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;
+
+  if (opt_create_keypair == TRUE) {
+    /* Create new key pair and exit */
+    silc_client_create_key_pair(opt_pkcs, opt_bits);
+    exit(0);
+  }
+
+  /* Read local configuration file */
+
+
+  /* Allocate new client */
+  ret = silc_client_alloc(&silc);
+  if (ret == FALSE)
+    goto fail;
+
+  /* Initialize the client */
+  silc->config = config;
+  ret = silc_client_init(silc);
+  if (ret == FALSE)
+    goto fail;
+
+  /* Run the client */
+  silc_client_run(silc);
+
+  /* Stop the client. This probably has been done already but it
+     doesn't hurt to do it here again. */
+  silc_client_stop(silc);
+  silc_client_free(silc);
+  
+  exit(0);
+
+ fail:
+  if (config)
+    silc_client_config_free(config);
+  if (silc)
+    silc_client_free(silc);
+  exit(1);
+}
diff --git a/apps/silc/silc.h b/apps/silc/silc.h
new file mode 100644 (file)
index 0000000..baa83b3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+  silc.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 SILC_H
+#define SILC_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 users'
+   home directory. This may override global configuration settings. */
+#define SILC_CLIENT_HOME_CONFIG_FILE ".silcrc"
+
+#endif
diff --git a/apps/silc/testi.conf b/apps/silc/testi.conf
new file mode 100644 (file)
index 0000000..72dbaed
--- /dev/null
@@ -0,0 +1,21 @@
+[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
new file mode 100644 (file)
index 0000000..e1fa600
--- /dev/null
@@ -0,0 +1,21 @@
+[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
diff --git a/apps/silcd/Makefile.am b/apps/silcd/Makefile.am
new file mode 100644 (file)
index 0000000..385b8cc
--- /dev/null
@@ -0,0 +1,42 @@
+#
+#  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
+
+bin_PROGRAMS = silcd
+
+silcd_SOURCES = \
+       protocol.c \
+       route.c \
+       server.c \
+       idlist.c \
+       command.c \
+       command_reply.c \
+       serverconfig.c \
+       serverid.c \
+       silcd.c \
+       server_version.c
+
+LDADD = -L. -L.. -L../lib -lsilc
+
+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
diff --git a/apps/silcd/command.c b/apps/silcd/command.c
new file mode 100644 (file)
index 0000000..eba9c41
--- /dev/null
@@ -0,0 +1,943 @@
+/*
+
+  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  2000/06/27 11:36:56  priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+/* 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(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(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(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(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(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),
+
+  { NULL, 0 },
+};
+
+/* List of pending commands. */
+SilcServerCommandPending *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. */
+
+void silc_server_command_pending(SilcCommand reply_cmd,
+                                SilcCommandCb callback,
+                                void *context)
+{
+  SilcServerCommandPending *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_server_command_pending_del(SilcCommand reply_cmd)
+{
+  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;
+      }
+    }
+  }
+}
+
+/* Free's the command context allocated before executing the command */
+
+static void silc_server_command_free(SilcServerCommandContext cmd)
+{
+  if (cmd) {
+    silc_command_free_payload(cmd->payload);
+    silc_free(cmd);
+  }
+}
+
+/* Sends command 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)
+{
+  SilcBuffer sp_buf, 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);
+  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 */
+
+static void 
+silc_server_command_send_status_reply(SilcServerCommandContext cmd,
+                                     SilcCommand command,
+                                     SilcCommandStatus status)
+{
+  SilcBuffer sp_buf, 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);
+  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. */
+
+SILC_SERVER_CMD_FUNC(whois)
+{
+  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_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);
+    } else {
+      nick = strdup(tmp);
+    }
+  } else {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
+                                         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_WHOIS,
+                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
+      if (nick)
+       silc_free(nick);
+      if (server)
+       silc_free(server);
+      goto out;
+    }
+    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) {
+
+    /* 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) {
+
+      goto ok;
+    }
+    
+    /* 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;
+      }
+      goto ok;
+    }
+
+    silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
+                                       SILC_STATUS_ERR_NO_SUCH_NICK,
+                                       tmp, strlen(tmp));
+    goto out;
+  }
+
+ ok:
+  /* XXX, works only for local server info */
+
+  /* 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);
+
+  /* XXX */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    char nh[256], uh[256];
+    SilcSocketConnection hsock;
+
+    memset(uh, 0, sizeof(uh));
+    memset(nh, 0, sizeof(nh));
+
+    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);
+
+    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));
+
+  } 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);
+
+  silc_free(id_string);
+  silc_buffer_free(packet);
+  silc_free(sp_buf);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+SILC_SERVER_CMD_FUNC(whowas)
+{
+}
+
+SILC_SERVER_CMD_FUNC(identify)
+{
+  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);
+    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);
+    }
+  } 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;
+    }
+    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;
+    }
+
+    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);
+
+  silc_free(id_string);
+  silc_buffer_free(packet);
+  silc_free(sp_buf);
+
+ out:
+  if (nick)
+    silc_free(nick);
+  if (server)
+    silc_free(server);
+  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)
+{
+  if (strchr(nick, '\\')) return TRUE;