Commits

Anonymous committed 9d06673

A new package 'eicq' - an ICQ client for XEmacs

  • Participants
  • Tags eicq-1_01

Comments (0)

Files changed (36)

+2000-08-16  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* version 0.2.6 released - XEmacs package 1.01
+
+	* etc/world: added info about adding your own UIN
+
+	* INSTALL: ditto
+
+	* INSTALL: fixed typo
+
+2000-08-15  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* verion 0.2.5 released - XEmacs package 1.00
+
+	* package-info-in: changed description
+
+2000-07-18  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* eicq.texi: new file
+
+	* INSTALL: total re-write for XEmacs packages
+
+	* package-info.in: new file
+
+	* eicq-toolbar.el: updated license
+
+	* eicq.el: updated license
+
+	* Makefile: total re-write for XEmacs packages
+
+2000-06-26  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* README: moved install stuff to INSTALL
+
+	* INSTALL: new file
+
+	* world: my UIN was wrong :-(  fixed
+
+2000-06-07  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* version 0.2.4 released
+
+	* README: updated install instructions
+
+2000-06-06  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* eicq.el (eicq-buddy-mode-hook): add-hook 
+	eicq-install-buddy-toolbar
+	(eicq-log-mode-hook): add-hook eicq-install-log-toolbar
+	(eicq-email-author): change address
+
+	* Makefile: updated for a more "system-wide" install
+
+	* eicq-user-install.sh: New file
+
+	* eicq-toolbar.el: New file
+
+2000-06-05  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* eicq.el (eicq-do-message-helper): added new sounds
+	(eicq-do-message-helper): pager now decodes message
+	(eicq-auth-sound): New variable
+	(eicq-emailx-sound): New variable
+	(eicq-pager-sound): New variable
+	(eicq-global-map-set): new key bindings added to eicq-buddy-map 
+	and eicq-log-map 
+
+2000-06-01  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* eicq.el (eicq-main-menu): "Update Meta Info" pointed to
+	the wrong place.  Now points to eicq-update-meta-info
+
+2000-05-30  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* version 0.2.3 released
+
+2000-05-13  Steve Youngs  <youngs_s@ozlinx.com.au>
+
+	* eicq.el
+	(eicq-sound): New customization group
+	(eicq-sound-directory): New variable
+	(eicq-message-sound): New variable
+	(eicq-chat-sound): New variable
+	(eicq-url-sound): New variable
+	(eicq-buddy-sound): New variable
+
+1999-08-12  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.2.2 released
+
+	* eicq.el
+	(eicq-world-update): extent for alias (from Erik)
+	(eicq-log-buddy-url): new function, extent for url (from Erik)	
+	(eicq-url-map): new variable
+	(eicq-alias-map): new variable
+	(eicq-send-message-via-mouse): new function
+	(eicq-send-message-alias-here-via-mouse): removed
+	(world-mode): new mode and world-* helper
+	(world-find): new function
+	(world-sort): new function
+	(eicq-change-user): new function
+	(eicq-buddy-view-connected): update eicq-buddy-view
+	(eicq-status-bin): zero-padded
+	(eicq-bridge-buffer): store buffer id instead of name
+	(eicq-hide-window): new function
+	(eicq-email-author): carbon-copy to mailing list
+
+	* world: add sample records	
+
+1999-07-10  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.2.1 released.
+
+	* eicq.el
+ 	(eicq-do-kicked-out): remove relogin
+	(eicq-log-around): fixed to include first line
+	(eicq-do-message-helper): display authorization message
+	(eicq-bridge-mode): fixed kill-buffer-query-functions
+
+1999-06-29  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.2.0 released.
+	- ICQ version 5 protocol
+	- outgoing delay queue and resent queue
+	- meta user info query and update
+
+	* eicq.el
+	(eicq-log-mark-*): mark all log in a region
+	(eicq-keep-alive-stop): use start-itimer
+	(eicq-pack-contact-list): use smaller packet
+	(eicq-do-message-helper): receive contact list transfer
+	(eicq-register-new-user): new function
+	(eicq-do-info-ext): fixed age bug
+	(eicq-world-update): RC file not closed if current or modified
+	(eicq-auto-away-timeout-set): added auto-na
+	(eicq-bin-alias): optional from
+	(eicq-alias-around): cleaned up
+	(eicq-log-around): new function
+	(eicq-send-message): encode only outgoing part
+	(eicq-forward-message-around): new function
+	(eicq-change-status): allow only valid statues
+	(eicq-logout): clear buddy buffer when offline
+	(eicq-change-status): change only if different
+	(eicq-valid-packet-p): integrated into eicq-do
+	(eicq-ack-alist): removed
+	(eicq-ack): integrated into eicq-do
+	(eicq-pack-login): fixed initial status
+	(eicq-relogin): fixed relogin initial status
+	(eicq-query-info): skip nil local info
+	
+	- more debugging and profiling counters
+	eicq-dropped-packet-counter
+	eicq-resend-packet-counter
+	eicq-auto-relogin-counter
+
+	* README: split into readme.developer
+	* README.developer: new
+	* udp2tcp.cc: protocol version 5
+
+1999-06-16  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.1.3 released.
+
+	* eicq.el
+	(eicq-spliter): new function
+	(eicq-send-message): send long messages
+	(eicq-do-info): fixed authorization check
+	(eicq-pack-update-info): new function
+	(eicq-do-search-end): new function
+	(eicq-auto-na-timeout-set): new function
+	(eicq-current-seq-num): follow micq, start from 0 instead of 1
+	(eicq-do-kicked-out): graceful relogin
+	(eicq-login): login only when offline
+	(eicq-buddy-getf): new function
+	(eicq-buddy-putf): new function
+	(eicq-int-byte): new function
+	(eicq-network-separator): eicq-trimmed-packet
+	(eicq-pack-contact-list): fixed random uin
+	(eicq-network-filter): fixed bug in checking validity of packets
+	(eicq-do-message-hook): new variable
+	(eicq-do-status-update-hook): new variable
+	(eicq-hex-bin): downcase uppercase
+	(eicq-process-alias-input): new function
+
+	: select
+	(eicq-group-select-aliases)
+	(eicq-select-alias-around)
+	(eicq-buddy-select-all-in-view)
+	(eicq-buddy-select-all-in-view-by-status)
+	(eicq-buddy-select-all-in-view-by-regexp)
+	(eicq-buddy-selected-in-view)
+	(eicq-select-alias-here)
+
+	: use length prefix instead of delimiting magic string
+	- (eicq-redo-hex)
+	- (eicq-network-filter)
+	- (eicq-network-separator)
+	- (eicq-send)
+	- (eicq-magic-string)
+
+	* README
+	: updated for new resource file
+	: fixed wrong assumption about eicq-pack-contact-list
+
+	* udp2tcp.cc (debug_socket):
+	: use length prefix instead of delimiting magic string
+	: remove usleep	
+
+1999-06-01  Stephen Tse  <stephent@sfu.ca>
+	
+	* version 0.1.2 released.
+
+	* eicq.el:
+	- add `encode/decode-coding-string'
+
+1999-05-22  Stephen Tse  <stephent@sfu.ca>
+
+	* eicq.el: 
+	(eicq-send-contact-list): make interactive
+	(eicq-main-menu): add "Resend contact list"
+
+1999-05-18  Stephen Tse  <stephent@sfu.ca>
+
+	* Makefile: brand new from Erik Arneson <erik@mind.net>	
+
+	* README:
+	- change installation steps for new Makefile
+	- mention mailing list and `eicq-log-new-file' in tips
+
+	* eicq.el: merge patches from Erik Arneson <erik@mind.net>
+	- add confirmation for sending blank message/url
+	- `eicq-send-message-alias-here-via-mouse'
+	- `browse-url-at-point' and other key bindings
+
+	* eicq.el (eicq-message-types): one byte instead of two
+
+1999-05-12  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.1.1 released.
+
+1999-05-12  Stephen Tse  <stephent@sfu.ca>
+
+	* version 0.1 released.
+	
+
+===========================================================
+As of the latest version, I develop and test eicq _only_ on
+my system, XEmacs 21.2.x & 21.1.x in GNU/Linux. I use some 
+XEmacs 20 specific codes and thus eicq will not run in other 
+versions of Emacs without modifications. Feel free to test 
+and port it in other platforms, and send me the patch.
+===========================================================
+
+This installation is definitely a 'no-brainer'
+
+1 - cd /usr/local/lib/xemacs/xemacs-packages RET (or where your package
+      root is)
+2 - tar zxvf /path/to/eicq-1.00-pkg.tar.gz RET
+
+Yeah, I know, you're swearing at me because you've already unpacked the
+thing to /usr/local/src.  Well life is full of things like that... :P
+
+Hold it, you're not finished yet...
+
+Add the following to your ~/.emacs 
+---------- cut ----------
+
+(require 'eicq)
+(require 'eicq-toolbar)
+(setq eicq-user-alias "me")
+(eicq-world-update)
+
+---------- cut ----------
+
+As the user that will be using eicq, run...
+
+/usr/local/lib/xemacs/xemacs-packages/lib-src/eicq-user-install.sh
+
+That will set up the needed stuff in your home directory.
+
+Adding your own UIN:
+-------------------
+~/.eicq/world is the file that stores not only the UIN's of your
+friends but also your own UIN.
+
+Just edit ~/.eicq/world and change the UIN beside 'me' to reflect your
+own UIN.
+
+Of course you can change the alias ('me') of your own UIN to be whatever
+you want.  But you'll have to change the (setq eicq-user-alias "...") line
+in your ~/.emacs to match.
+
+
+Ok, now you're done.
+
+Fire up XEmacs, run M-x eicq-login and M-x eicq-show-window. 
+There you go! Use XEmacs eicq menu to send messages,
+authorize, customize... For new XEmacs users, you can press
+C-h t for a tutorial on XEmacs. Don't hesitate to message me
+a success report or mail me your screenshots!
+# Makefile for eicq code
+
+# This file is part of XEmacs.
+
+# XEmacs is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any
+# later version.
+
+# XEmacs is distributed in the hope that it will be useful, but WITHOUT
+# 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 XEmacs; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+VERSION = 1.01
+AUTHOR_VERSION = 0.2.6
+MAINTAINER = Steve Youngs <youngs_s@ozlinx.com.au>
+PACKAGE = eicq
+PKG_TYPE = regular
+REQUIRES = xemacs-base mail-lib
+CATEGORY = comm
+
+ELCS = eicq.elc eicq-toolbar.elc
+
+DATA_FILES = $(wildcard etc/*.xpm) etc/world
+DATA_DEST = $(PACKAGE)
+
+INFO_FILES = $(PACKAGE).info
+TEXI_FILES = $(PACKAGE).texi
+MANUALS = $(PACKAGE)
+
+CC = g++
+CFLAGS = -O2 -Wall
+STRIP = /usr/bin/strip
+
+BIN = udp2tcp
+USERSH = eicq-user-install.sh
+
+LIBSRC_FILES = $(BIN) $(USERSH)
+
+EXTRA_SOURCES = README NEWS INSTALL TODO $(BIN).cc
+
+include ../../XEmacs.rules
+
+GENERATED += custom-load.elc
+
+all:: $(BIN) $(ELCS) auto-autoloads.elc custom-load.elc $(INFO_FILES)
+
+udp2tcp: $(BIN).cc
+	${CC} -o $(BIN) $(BIN).cc ${CFLAGS}
+	${STRIP} $(BIN)
+
+srckit: srckit-std
+
+binkit: binkit-common
+
+
+-*- outline -*-
+
+* from 0.2.4 to 0.2.5
+
+** entered into the mainstream XEmacs packages
+
+* from 0.2.3 to 0.2.4
+
+** clickety click toolbars
+** new locations for files (a system wide approach)
+** more sound
+
+* from 0.2.2 to 0.2.3
+
+** sound added
+
+* from 0.2.1 to 0.2.2
+
+** highlight alias and url (from Erik)
+** world-mode
+** eicq-change-user, eicq-hide-window
+
+* from 0.2.0 to 0.2.1
+
+** remove relogin, more stable
+
+* from 0.1.3 to 0.2.0
+
+** ICQ version 5 protocol
+** meta user info query and update
+** register new user, change password
+** receive contact list transfer
+** forward message
+** more stable (less kicked out)
+
+* from 0.1.2 to 0.1.3
+
+** restructure of eicq-world
+** use resource file instead of lisp structure
+** new files in package: NEWS and world (resource file)
+** alias selection as temporary group
+** fixed random popping up uin
+** safer udp2tcp: use length prefix instead of delimiting magic string
+** eicq-do-status-update-hook and eicq-do-message-hook for customization
+** split and send long messages
+** search users, update basic info and extended info
+** relogin quiently after being kicked out
+** auto-na
+-*- outline -*-
+
+* Introduction
+
+http://users.ozlinx.com.au/~youngs_s/eicq/README (version 0.2.5)
+
+This file is best viewed under XEmacs. Press C-c C-o now to
+see the outline of topics. You can use XEmacs menu to
+navigate and hide/show different topics. (C-c means Control
+and c; M-x means Meta and x.)
+
+I publish this package in the hope of making more people
+appreciate XEmacs and Lisp.
+
+You can contact me for anything at <youngs_s@ozlinx.com.au>.
+
+* Feature list
+
+eicq uses version 5 protocol and it can:
+
+- send message/url to single or multi users or group
+- authorize, search, query and update meta user info
+- receive contact list transfer
+- register new user, change password
+
+eicq cannot yet transfer files, chat, or anything that need
+direct TCP connection. Of course, with XEmacs behind you'll
+also get:
+
+- interactive customization through XEmacs Custom widgets
+- key binding, "scripting" in elisp
+  (the language eicq is written in)
+- send/receive multi-lingual languages (with XEmacs MULE)
+- a way of life
+
+* Installation
+See the file 'INSTALL' in this directory.
+
+You can also subscribe to eicq mailing list by sending an
+email to majordomo@aarg.net with "subscribe eicq" in the
+body.
+
+Have fun!
+
+* Faq and tips
+
+** password
+
+How to save password?
+
+M-x customize-variable RET eicq-user-password
+Or simply, (setq eicq-user-password "mypassword") in .emacs
+
+** "M" vs "m" (send-message)
+
+Commands in capital letters prompt you for aliases/uin while
+those in small letters search for aliases/uin around the
+cursor in eicq buffers, and perform actions on them. For
+example, "m" inside an incoming message acts like a reply.
+
+** alias vs uin
+
+"SteveY" is my alias while "34307457" is my uin. In all prompts
+of entering an icq person, you can enter either an alias or
+an uin, although only alias completing read is provided. 
+(Press TAB when entering an alias!)
+
+** message/alias history
+
+Use M-p/M-n or UP/DOWN to navigate history in prompts of
+entering alias or messages.
+Use M-r/M-s to search history.
+
+** log file size
+
+Watch out for monster eicq-log buffer size! Use M-x
+eicq-log-new-file occasionally.
+
+** hooks
+
+If you want to customize anything fancy:
+
+`eicq-do-message-hook'
+`eicq-do-status-update-hook'
+
+Interactive with eliza, forward messages to cell phone,
+launch a bomb, or whatever you like.
+
+** newline
+
+How to enter new lines in minibuffer? Type "Hello",
+control-Q 12 "world", to get:
+
+Hello
+world
+
+** ~/.emacs tips
+
+(resize-minibuffer-mode 1)
+
+
+-*- outline -*-
+
+* Last Updated: Aug 12, 2000
+
+Here's a list of things I may or may not get around to putting
+into eicq.
+
+This list is not in order of importance or any kind of order at all.
+And just because something is on the list doesn't mean that it will
+become part of eicq.
+
+Feel free to do any of these things and send me a patch
+<youngs_s@ozlinx.com.au>
+
+* Todo list
+
+** write the info docs (this one probably is a priority)
+
+** hilarious auto reply messages
+
+** face colors for light background
+
+** figure out why sometime being kicked out after long inactivity
+
+** builtin udp support in emacs
+
+	write udp2tcp as an 'emodule'
+
+** complete country codes
+
+** set/retrieve away/occ message in server?
+
+** visible/invisible list
+
+** direct TCP, file transfer/chat
+
+** invisibility probing (validate published ip?)
+
+** send other special types of messages
+
+** pop up/permanent window for entering long message
+
+** multi-user login
+
+	eicq can successfully login server using different accounts,
+	and can then receive messages from both accounts.
+
+	However, after some brief attempts, eicq cannot send
+	messages with the first account after the second account is
+	logged in. ICQ server perhaps checks ip address as well to
+	avoid spoofing. licq seems to be able to achieve that. Is
+	spoofing only allowed in TCP mode?
+
+** implement ack'ing in udp2tcp
+
+	sometimes loading gnus/w3 makes single-threaded emacs crawl
+	and get kicked out of icq server because of failures of
+	ack'ing packets
+
+** cute glyph for different statuses
+
+** eliza (doctor) minor mode, auto-chatting/messaging
+
+** store offline messages and send them when online
+
+** security problem of `eicq-user-password' in emacs
+
+** openssl support
+
+** port udp2tcp to other platforms
+
+	maybe not necessary if written as a module??

File eicq-toolbar.el

+;; eicq-toolbar.el   -*-Emacs-Lisp-*-
+;; $Id$
+;; Copyright (C) 2000 Steve Youngs
+
+
+;; Author: Steve Youngs <youngs_s@ozlinx.com.au>
+;;
+;; Keywords: eicq, toolbar, comm
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by the
+;; Free Software Foundation; either version 2, or (at your option) any
+;; later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but WITHOUT
+;; 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 XEmacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; Commentary:
+
+;;            A toolbar for eicq
+
+;; Set some variables for the icon directory and files
+
+(defvar eicq-icon-directory 
+  "/usr/local/lib/xemacs/xemacs-packages/etc/eicq/"
+  "The directory where the icon data files for eicq are installed.")
+
+(defvar eicq-password-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "password.xpm" eicq-icon-directory))
+  "A password toolbar icon.")
+
+(defvar eicq-show-log-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "show.xpm" eicq-icon-directory))
+  "A show-window toolbar icon.")
+
+(defvar eicq-hide-log-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "hide.xpm" eicq-icon-directory))
+  "A hide log toolbar icon.")
+
+(defvar eicq-send-message-here-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "send-message-here.xpm" eicq-icon-directory))
+  "A send message toolbar icon.")
+
+(defvar eicq-send-message-around-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "send-message-around.xpm" eicq-icon-directory))
+  "A send message toolbar icon.")
+
+(defvar eicq-send-url-here-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "url-here.xpm" eicq-icon-directory))
+  "A send URL toolbar icon.")
+
+(defvar eicq-send-url-around-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "url-around.xpm" eicq-icon-directory))
+  "A send URL toolbar icon.")
+
+(defvar eicq-query-info-here-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "query-here.xpm" eicq-icon-directory))
+  "A query info here toolbar icon.")
+
+(defvar eicq-query-info-around-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "query-around.xpm" eicq-icon-directory))
+  "A query info here toolbar icon.")
+
+(defvar eicq-update-info-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "update-info.xpm" eicq-icon-directory))
+  "A update info toolbar icon.")
+
+(defvar eicq-search-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "search.xpm" eicq-icon-directory))
+  "A search toolbar icon.")
+
+(defvar eicq-authorize-here-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "authorize-here.xpm" eicq-icon-directory))
+  "A authorize toolbar icon.")
+
+(defvar eicq-authorize-around-icon 
+  (toolbar-make-button-list 
+   (expand-file-name "authorize-around.xpm" eicq-icon-directory))
+  "A authorize toolbar icon.")
+
+(defvar eicq-login-icon
+  (toolbar-make-button-list
+   (expand-file-name "login.xpm" eicq-icon-directory))
+  "A login toolbar icon")
+
+(defvar eicq-logout-icon
+  (toolbar-make-button-list
+   (expand-file-name "logout.xpm" eicq-icon-directory))
+  "A logout toolbar icon")
+
+(defvar eicq-disconnect-icon
+  (toolbar-make-button-list
+   (expand-file-name "disconnect.xpm" eicq-icon-directory))
+  "A disconnect toolbar icon")
+
+(defvar eicq-mark-log-read-icon
+  (toolbar-make-button-list
+   (expand-file-name "read.xpm" eicq-icon-directory))
+  "Mark log read toolbar icon")
+
+(defvar eicq-mark-log-unread-icon
+  (toolbar-make-button-list
+   (expand-file-name "unread.xpm" eicq-icon-directory))
+  "Mark log unread toolbar icon")
+
+(defvar eicq-next-log-icon
+  (toolbar-make-button-list
+   (expand-file-name "next.xpm" eicq-icon-directory))
+  "Next log item toolbar icon")
+
+(defvar eicq-previous-log-icon
+  (toolbar-make-button-list
+   (expand-file-name "previous.xpm" eicq-icon-directory))
+  "Previous log item toolbar icon")
+
+(defvar eicq-new-log-icon
+  (toolbar-make-button-list
+   (expand-file-name "new-log.xpm" eicq-icon-directory))
+  "New log file toolbar icon")
+
+(defvar eicq-contract-log-icon
+  (toolbar-make-button-list
+   (expand-file-name "contract.xpm" eicq-icon-directory))
+  "Contract the log toolbar icon")
+
+(defvar eicq-expand-log-icon
+  (toolbar-make-button-list
+   (expand-file-name "expand.xpm" eicq-icon-directory))
+  "Expand the log toolbar icon")
+
+;; Define the functions for the toolbar
+
+(defun eicq-toolbar-change-password (password)
+  "Change password from the toolbar."
+  (interactive (list (read-passwd "Password: " 'confirm)))
+  (eicq-change-password password))
+
+(defun eicq-toolbar-show-log ()
+  "Show the log window from the toolbar"
+  (interactive)
+  (eicq-show-window))
+
+(defun eicq-toolbar-hide-log ()
+  "Hide the log window from the toolbar"
+  (interactive)
+  (eicq-hide-window))
+
+(defun eicq-toolbar-send-message-here ()
+  "Send message from toolbar"
+  (interactive)
+  (eicq-send-message-alias-here))
+
+(defun eicq-toolbar-send-message-around ()
+  "Send message from toolbar"
+  (interactive)
+  (eicq-send-message))
+
+(defun eicq-toolbar-send-url-here ()
+  "Send URL from the toolbar"
+  (interactive)
+  (eicq-send-url-alias-here))
+
+(defun eicq-toolbar-send-url-around ()
+  "Send URL from the toolbar"
+  (interactive)
+  (eicq-send-url))
+
+(defun eicq-toolbar-query-info-here ()
+  "Query info from the toolbar"
+  (interactive)
+  (eicq-query-info-alias-here))
+
+(defun eicq-toolbar-query-info-around ()
+  "Query info from the toolbar"
+  (interactive)
+  (eicq-query-info))
+
+(defun eicq-toolbar-update-info ()
+  "Update meta info from the toolbar"
+  (interactive)
+  (eicq-update-meta-info))
+
+(defun eicq-toolbar-search (nick-name first-name last-name email)
+  "Search from the toolbar"
+  (interactive "sNick-name: \nsFirst-name: \nsLast-name: \nsEmail: \n")
+  (eicq-search nick-name first-name last-name email))
+
+(defun eicq-toolbar-authorize-here ()
+  "Authorize from the toolbar"
+  (interactive)
+  (eicq-authorize-alias-here))
+
+(defun eicq-toolbar-authorize-around ()
+  "Authorize from the toolbar"
+  (interactive)
+  (eicq-authorize))
+
+(defun eicq-toolbar-login ()
+  "Login from the toolbar"
+  (interactive)
+  (eicq-login))
+
+(defun eicq-toolbar-logout ()
+  "Logout from the toolbar"
+  (interactive)
+  (eicq-logout))
+
+(defun eicq-toolbar-disconnect ()
+  "Disconnect from the toolbar"
+  (interactive)
+  (eicq-disconnect))
+
+(defun eicq-toolbar-log-read ()
+  "Mark log item read from the toolbar"
+  (interactive)
+  (eicq-log-mark-read))
+
+(defun eicq-toolbar-log-unread ()
+  "Mark log item unread from the toolbar"
+  (interactive)
+  (eicq-log-mark-unread))
+
+(defun eicq-toolbar-next-log ()
+  "Next log item from the toolbar"
+  (interactive)
+  (eicq-log-next 1))
+
+(defun eicq-toolbar-previous-log ()
+  "Previous log item from the toolbar"
+  (interactive)
+  (eicq-log-previous 1))
+
+(defun eicq-toolbar-new-log ()
+  "New log file from the toolbar"
+  (interactive)
+  (eicq-log-new-file))
+
+(defun eicq-toolbar-contract-log ()
+  "Contract the log from the toolbar"
+  (interactive)
+  (eicq-log-contract))
+
+(defun eicq-toolbar-expand-log ()
+  "Expand the log from the toolbar"
+  (interactive)
+  (eicq-log-expand))
+
+;; Now define the toolbar
+
+(defvar eicq-buddy-toolbar
+  '([eicq-password-icon 
+     eicq-toolbar-change-password t "Change password"]
+    [eicq-show-log-icon 
+     eicq-toolbar-show-log t "Show log"]
+    [eicq-hide-log-icon 
+     eicq-toolbar-hide-log t "Hide log"]
+    [eicq-send-message-here-icon 
+     eicq-toolbar-send-message-here t "Send message here"]
+    [eicq-send-message-around-icon
+     eicq-toolbar-send-message-around t "Send message..."]
+    [eicq-send-url-here-icon 
+     eicq-toolbar-send-url-here t "Send URL here"]
+    [eicq-send-url-around-icon 
+     eicq-toolbar-send-url-around t "Send URL..."]
+    [eicq-query-info-here-icon 
+     eicq-toolbar-query-info-here t "Query info here"]
+    [eicq-query-info-around-icon
+     eicq-toolbar-query-info-around t "Query info..."]
+    [eicq-update-info-icon 
+     eicq-toolbar-update-info t "Update info"]
+    [eicq-search-icon 
+     eicq-toolbar-search t "Search"]
+    [eicq-authorize-here-icon 
+     eicq-toolbar-authorize-here t "Authorize here"]
+    [eicq-authorize-around-icon
+     eicq-toolbar-authorize-around t "Authorize..."]
+    [eicq-login-icon
+     eicq-toolbar-login t "Login"]
+    [eicq-logout-icon
+     eicq-toolbar-logout t "Logout"]
+    [eicq-disconnect-icon
+     eicq-toolbar-disconnect t "Disconnect"])
+  "The clickety click eicq buddy toolbar")
+
+(defvar eicq-log-toolbar
+  '([eicq-send-message-here-icon 
+     eicq-toolbar-send-message-here t "Send message here"]
+    [eicq-send-message-around-icon
+     eicq-toolbar-send-message-around t "Send message..."]
+    [eicq-send-url-here-icon 
+     eicq-toolbar-send-url-here t "Send URL here"]
+    [eicq-send-url-around-icon 
+     eicq-toolbar-send-url-around t "Send URL..."]
+    [eicq-query-info-here-icon 
+     eicq-toolbar-query-info-here t "Query info here"]
+    [eicq-query-info-around-icon
+     eicq-toolbar-query-info-around t "Query info..."]
+    [eicq-authorize-here-icon 
+     eicq-toolbar-authorize-here t "Authorize here"]
+    [eicq-authorize-around-icon
+     eicq-toolbar-authorize-around t "Authorize..."]
+    [eicq-mark-log-read-icon
+     eicq-toolbar-log-read t "Mark read"]
+    [eicq-mark-log-unread-icon
+     eicq-toolbar-log-unread t "Mark unread"]
+    [eicq-next-log-icon
+     eicq-toolbar-next-log t "Next"]
+    [eicq-previous-log-icon
+     eicq-toolbar-previous-log t "Previous"]
+    [eicq-new-log-icon
+     eicq-toolbar-new-log t "New log file"]
+    [eicq-contract-log-icon
+     eicq-toolbar-contract-log t "Contract log"]
+    [eicq-expand-log-icon
+     eicq-toolbar-expand-log t "Expand log"]
+    [eicq-login-icon
+     eicq-toolbar-login t "Login"]
+    [eicq-logout-icon
+     eicq-toolbar-logout t "Logout"]
+    [eicq-disconnect-icon
+     eicq-toolbar-disconnect t "Disconnect"])
+  "A clickety click eicq log buffer toolbar")
+
+(defcustom eicq-use-toolbar (if (featurep 'toolbar)
+				'default-toolbar
+			      nil)
+  "*If nil, do not use a toolbar.
+If it is non-nil, it must be a toolbar.  The five valid values are
+`default-toolbar', `top-toolbar', `bottom-toolbar',
+`right-toolbar', and `left-toolbar'."
+  :type '(choice (const default-toolbar)
+		 (const top-toolbar) (const bottom-toolbar)
+		 (const left-toolbar) (const right-toolbar)
+		 (const :tag "no toolbar" nil))
+  :group 'eicq-option)
+
+(defun eicq-install-buddy-toolbar ()
+  (and eicq-use-toolbar
+       (set-specifier (symbol-value eicq-use-toolbar)
+		      (cons 
+		       (current-buffer) eicq-buddy-toolbar))))
+
+(defun eicq-install-log-toolbar ()
+  (and eicq-use-toolbar
+       (set-specifier (symbol-value eicq-use-toolbar)
+		      (cons 
+		       (current-buffer) eicq-log-toolbar))))
+
+(provide 'eicq-toolbar)
+
+

File eicq-user-install.sh

+#! /bin/bash
+## eicq-user-install.sh   -*-Shell-script-*-
+## $Id$
+
+## Copyright (C) 2000 Steve Youngs
+
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT 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
+
+#----------------------------------------------------------------
+# Commentary:
+#    Installs the necessary files in your home directory to run eicq
+#----------------------------------------------------------------
+
+# Variables and constants
+MKDIR=/bin/mkdir
+INSTALL=/usr/bin/install
+RCFILE=/usr/local/lib/xemacs/xemacs-packages/etc/eicq/world
+RCDIR=${HOME}/.eicq
+
+# Code
+$MKDIR $RCDIR
+$INSTALL -m 600 $RCFILE $RCDIR
+
+echo "Don't forget to edit ${RCDIR}/world to your requirements"
+
+# eicq-user-install.sh ends here.
+;;; eicq.el --- ICQ client for Emacs
+;; $Id$
+;; Copyright (C) 1999 by Stephen Tse
+;; Copyright (C) 2000 Steve Youngs
+
+;; Original Author: Stephen Tse <stephent@sfu.ca>
+;; Maintainer: Steve Youngs <youngs_s@ozlinx.com.au>
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by the
+;; Free Software Foundation; either version 2, or (at your option) any
+;; later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but WITHOUT
+;; 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 XEmacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+
+;; Created: Aug 08, 1998
+;; Updated: Aug 16, 2000
+
+;; Version: 0.2.6
+;; Homepage: http://users.ozlinx.com.au/~youngs_s/eicq/
+;; Keywords: comm ICQ
+
+;;; Commentary:
+;;
+;; Clone of Mirabilis ICQ communication client.
+;;
+;; Entry points:
+;;   eicq-login
+;;   eicq-show-window
+;;   eicq-email-author
+;;   eicq-customize
+;;
+;;
+;; See README which comes with this file or at
+;;  http://users.ozlinx.com.au/~youngs_s/eicq/README
+;;
+;; This project is done without the consent of Mirabilis.
+;;
+
+;;; Code:
+
+(eval-when-compile
+  (require 'browse-url)
+  (require 'reporter)
+  (require 'outline))
+
+(defconst eicq-version "0.2.6"
+  "Version of eicq you are currently using.")
+
+(defgroup eicq nil
+  "Mirabilis ICQ communication client."
+  :group 'comm)
+
+(defgroup eicq-info nil
+  "Essential account info."
+  :group 'eicq)
+
+(defgroup eicq-meta nil
+  "User info stored in ICQ server.
+Run `eicq-update-meta-info' after changing any of these variables."
+  :group 'eicq)
+
+(defgroup eicq-option nil
+  "System settings and general preferences."
+  :group 'eicq)
+
+(defgroup eicq-log nil
+  "Message logging preferences."
+  :group 'eicq)
+
+(defgroup eicq-buddy nil
+  "Contact list preferences."
+  :group 'eicq)
+
+(defgroup eicq-sound nil
+  "Sound preferences."
+  :group 'eicq)
+
+(defun eicq-version ()
+  "Return the version of eicq you are currently using."
+  (interactive)
+  (message "eicq version %s" eicq-version))
+
+;;; Code - compatibility:
+
+;; MULE compatibility functions for 20.4
+
+(defun-when-void encode-coding-string (string encoding-system)
+  "Stub for compatibility with MULE."
+  string)
+
+(defun-when-void decode-coding-string (string encoding-system)
+  "Stub for compatibility with MULE."
+  string)
+
+;;; Code - user info:
+
+(defcustom eicq-user-alias "me"
+  "*Your alias in `eicq-world'.
+Run `eicq-world-update' after modifying this variable."
+  :group 'eicq-info)
+
+(defcustom eicq-user-password nil
+  "*Password for your ICQ account.
+Nil means prompt for entering password every time you login."
+ :group 'eicq-info)
+
+;;; Code - user meta info:
+
+(defcustom eicq-user-meta-nickname "e-i-c-q"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable.
+ICQ server refuses any string containing substring \"icq\"."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-firstname "XEmacs"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable.
+ICQ server refuses any string containing substring \"icq\"."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-lastname "Linux"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable.
+ICQ server refuses any string containing substring \"icq\"."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-primary-email "emacs@home.com"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable.
+ICQ server refuses any string containing substring \"icq\"."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-secondary-email "eicq@home.com"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-old-email "eicq@home.com"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-city nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-state nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-phone nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-fax nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-street nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-cell-phone nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-zipcode nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-country nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-web-aware t
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-hide-ip nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-authorization t
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-about
+  "Give a man a new Emacs command,
+  and he can hack for a night;
+Teach a man to make new Emacs commands,
+  and he can hack for a life time."
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-homepage 
+  "http://users.ozlinx.com.au/~youngs_s/eicq/"
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-age nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable.
+65535 = not entered."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-sex 'not-entered
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta
+  :type '(choice (item male) (item female)
+                 (item not-entered)))
+
+(defcustom eicq-user-meta-birth-year nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-birth-month nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-birth-day nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-language-1 0
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-language-2 0
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-language-3 0
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-city nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-state nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-phone nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-fax nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-address nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-company nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-department nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-position nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-user-meta-work-homepage nil
+  "*User info stored in ICQ server.
+Run `eicq-update-meta-info' after modifying this variable."
+  :group 'eicq-meta)
+
+(defcustom eicq-sound-directory 
+  "/usr/local/lib/xemacs/xemacs-packages/etc/sounds/"
+  "*Directory where sound files are kept."
+  :group 'eicq-sound
+  :type 'directory 
+  :tag "eicq-sound-directory")
+
+(defcustom eicq-message-sound nil
+  "*Incoming message sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Message Sound")
+
+(defcustom eicq-chat-sound nil
+  "*Incoming chat request sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Chat Request Sound")
+
+(defcustom eicq-url-sound nil
+  "*Incoming URL sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Incoming URL Sound")
+
+(defcustom eicq-buddy-sound nil
+  "*Buddy online notify sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Online Notify Sound")
+
+(defcustom eicq-auth-sound nil
+  "*Buddy online notify sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Authorize Sound")
+
+(defcustom eicq-emailx-sound nil
+  "*Buddy online notify sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Email Express Sound")
+
+(defcustom eicq-pager-sound nil
+  "*Buddy online notify sound"
+  :group 'eicq-sound
+  :type 'file
+  :tag "Pager Sound")
+
+;; Load the toolbar
+(add-hook 'eicq-buddy-mode-hook 'eicq-install-buddy-toolbar)
+(add-hook 'eicq-log-mode-hook 'eicq-install-log-toolbar)
+
+;;; Code - data conversion:
+
+;; hex  string based 16 (for debugging)
+;; bin  string based 2 (for network packet)
+;; uin  string based 10
+;; int  integer based 10
+;;
+;; Note 1: Special care should be taken in regard of endian-ness of `bin'.
+;;
+;; Note 2: `uin' is stored as a string because it is an 32-bit integer in
+;; network packets and it is larger than the integer representation in
+;; Emacs. Internally eicq uses `float' to do the conversion.
+
+(defun eicq-hex-bin (hex)
+  "Return a binary string from a hex string HEX.
+It ignores all non-lower letter hex characters, which makes reading string
+from `eicq-bin-pretty-hex' and netwrok debug convenient.  If the length of
+HEX is odd, ?0 is appended to its end."
+  (interactive "sHex: ")
+  (setq hex (downcase hex))
+  ;; stole from hexl.el
+  (while (string-match "[^a-f0-9]" hex)
+    (setq hex (replace-match "" t t hex)))
+
+  (let (hex-list high low bin)
+    (setq hex-list
+          (mapcar
+           (lambda (x)
+             (if (and (>= x ?0) (<= x ?9))
+                 (- x ?0) (+ 10 (- x ?a))))
+           hex))
+    (while hex-list
+      (setq high (pop hex-list))
+      (setq low (or (pop hex-list) 0))
+      (setq bin (concat bin (char-to-string
+                             (+ (* high 16) low)))))
+    (if (interactive-p) (kill-new bin))
+    bin))
+
+(defun eicq-bin-hex (bin)
+  "Return a hex string from a binary string BIN."
+  (mapconcat
+   (lambda (x) (format "%02x" x))
+   bin nil))
+
+(defun eicq-bin-pretty-hex (bin)
+  "Return a pretty-printed hex string from a binary string BIN."
+  (let ((i 0))
+    (mapconcat
+     (lambda (x)
+       (format
+        ;; insert a space every 4 hex = 2 bin
+        (if (evenp (incf i)) "%02x " "%02x")
+        x))
+     bin nil)))
+
+(defun eicq-pretty-hex (hex)
+  "Return a pretty-printed hex string from a HEX string."
+  (eicq-bin-pretty-hex (eicq-hex-bin hex)))
+
+(defun eicq-int-bin (int)
+  "Return a 2-byte binary string from an integer INT."
+  ;; stole from hexl.el
+  (let ((low (logand int 255))
+        (high (ash int -8)))
+    (concat (char-to-string low) (char-to-string high))))
+
+(defun eicq-bin-int (bin &optional from)
+  "Return an integer from a binary string BIN.
+Consider only the first two characters from FROM to FROM+1 in BIN.  Useful
+in parsing packet.  Nil FROM means 0."
+  ;; stole from hexl.el
+  (let* ((from (or from 0))
+         (low (aref bin from))
+         (high (aref bin (1+ from))))
+    (+ low (ash high 8))))
+
+(defun eicq-byte-int (bin &optional at)
+  "Return an integer from a character byte in a binary string BIN.
+Consider the character byte at AT in BIN.  Useful in parsing packet.  Nil
+AT means 0."
+  (char-to-int (aref bin (or at 0))))
+
+(defun eicq-int-byte (int)
+  "Return a binary string of one character byte from an integer INT."
+  (if (< int 256)
+      (char-to-string (int-to-char int))))
+
+(defun eicq-uin-bin (uin)
+  "Return a binary string for an UIN.
+Return \"00000000\" in binary string if conversion fails."
+  ;; stole from Eric's xmath.el
+  (let (x y)
+    (setq x (condition-case nil
+                (eval (read (format "(float %s.0)" uin)))
+              (error 0)))
+
+    ;; least value byte
+    (setq y (char-to-string (logand (truncate (mod x 65536)) 255)))
+    ;; shift by 8 bit = 1 byte
+    (setq x (/ x 256))
+    (setq y (concat y (char-to-string
+                       (logand (truncate (mod x 65536)) 255))))
+    (setq x (/ x 256))
+    (setq y (concat y (char-to-string
+                       (logand (truncate (mod x 65536)) 255))))
+    (setq x (/ x 256))
+    (setq y (concat y (char-to-string
+                       (logand (truncate (mod x 65536)) 255))))))
+
+(defun eicq-bin-uin (bin &optional from)
+  "Return an uin from a binary string BIN.
+Consider only the first four characters from FROM to FROM+3 in BIN.
+Nil FROM means 0."
+  ;; stole from Eric's xmath.el
+  (let ((from (or from 0)))
+    (format "%.f"
+            (+ (float (char-to-int (aref bin from)))
+               (* (float (char-to-int (aref bin (+ from 1)))) 256)
+               (* (float (char-to-int (aref bin (+ from 2)))) 256 256)
+               (* (float (char-to-int (aref bin (+ from 3)))) 256 256
+                  256)))))
+
+(defun eicq-bin-ip (bin from)
+  "Return an IP address from a binary string BIN.
+Consider only the first four characters from FROM to FROM+3 in BIN."
+  (mapconcat
+   (lambda (x)
+     (int-to-string (char-to-int x)))
+   (substring bin from (+ from 4)) "."))
+
+;;; Code - utilities:
+
+(defun eicq-completing-read
+  (prompt table &optional predicate require-match initial-contents history)
+  "Args: PROMPT, TABLE, PREDICATE, REQUIRE-MATCH, INITIAL-CONTENTS, HISTORY.
+Same as `completing-read' but accepts strings as well as obarray."
+  (completing-read
+   prompt
+   (if (vectorp table)
+       table (mapcar 'list table))
+   predicate require-match initial-contents history))
+
+;;;### autoload
+
+(defun eicq-customize ()
+  "Interactively customize settings and preferences."
+  (interactive)
+  (customize-group 'eicq))
+
+;;;###autoload
+
+(defun eicq-email-author ()
+  "Email comments or money to author."
+  (interactive)
+  (let ((reporter-prompt-for-summary-p t)
+        (salutation "
+;; Bug report, feature request, patch, thank-you card... are all welcome.
+;; Flammage is automagically > /dev/null.
+
+
+
+Dear Steve,
+"))
+    (reporter-submit-bug-report
+     "youngs_s@ozlinx.com.au"
+     (format "eicq v%s" eicq-version) nil nil nil salutation)))
+
+(defun eicq-browse-homepage ()
+  "Browse eicq homepage for news and files."
+  (interactive)
+  (browse-url "http://users.ozlinx.com.au/~youngs_s/eicq/"))
+
+(defcustom eicq-coding-system nil
+  "*Coding for incoming and outgoing messages.
+This feature is supported only in Emacs with MULE.
+Nil means not to use any codings.
+See `list-coding-systems'."
+  :group 'eicq-option
+  :type
+  (append '(choice (item nil))
+          (if (fboundp 'coding-system-list)
+              (mapcar
+               (lambda (x) (list 'item x))
+               (coding-system-list)))))
+
+(defun eicq-encode-string (string)
+  "Return a encoded string from STRING with DOS stuff added.
+Encode string with `eicq-coding-system'."
+  (encode-coding-string
+   ;; add DOS stuff
+   ;; "0d" instead to avoid use of ^M
+   ;; which messes up with outline mode
+   (replace-in-string string "\x0a" "\x0d\x0a")
+   eicq-coding-system))
+
+(defun eicq-decode-string (string)
+  "Return a decoded string from STRING with DOS stuff removed.
+It also quote character % to make `format' happy in `eicq-log'.
+Decode string with `eicq-coding-system'."
+  (decode-coding-string
+   ;; quote %
+   (replace-in-string
+    ;; remove DOS stuff
+    ;; "0d0a" instead to avoid use of ^M
+    ;; which messes up with outline mode
+    (replace-in-string string "\x0d\x0a" "\x0a")
+    "%" "%%")
+   eicq-coding-system))
+
+(defun eicq-spliter (x)
+  "Split a long message X into parts of maximum length `eicq-message-max-size'.
+Only split at whitespace."
+  (loop
+    with i = eicq-message-max-size
+    while (> (length x) i)
+    do (while (and (not (memq (aref x (incf i -1)) '(?  ?\t)))
+                   ;; at least half, to safe guard
+                   (> i (/ eicq-message-max-size 2))))
+    collect (substring x 0 i) into parts
+    do (setq x (substring x i))
+    finally return (nconc parts (list x))))
+
+(defvar eicq-monthnames
+  ["0" "Jan" "Feb" "Mar" "Apr" "May" "Jun"
+   "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"])
+
+(defvar eicq-country-code
+  '((65535 . "not entered")
+    (93 . "Afghanistan")
+    (355 . "Albania")
+    (213 . "Algeria")
+    (376 . "Andorra")
+    (244 . "Angola")
+    (672 . "Antarctic Aus Territory")
+    (599 . "Antilles")
+    (54 . "Argentina")
+    (374 . "Armenia")
+    (297 . "Aruba")
+    (247 . "Ascension Island")
+    (61 . "Australia")
+    (43 . "Austria")
+    (994 . "Azerbaijan")
+    (351 . "Azores")
+    (973 . "Bahrain")
+    (890 . "Bangladesh")
+    (809 . "Barbados")
+    (257 . "Barundi")
+    (375 . "Belarus")
+    (32 . "Belgium")
+    (229 . "Belize")
+    (501 . "Belize")
+    (975 . "Bhutan")
+    (591 . "Bolivia")
+    (387 . "Bosnia Hercegovina")
+    (267 . "Botswana")
+    (55 . "Brazil")
+    (673 . "Brunei Darussalam")
+    (226 . "Bukina Faso")
+    (359 . "Bulgaria")
+    (855 . "Cambodia")
+    (237 . "Cameroon")
+    (107 . "Canada")
+    (238 . "Cape Verde Islands")
+    (236 . "Central African Republic")
+    (235 . "Chad")
+    (56 . "Chile")
+    (86 . "China")
+    (672 . "Christmas Island")
+    (672 . "Cocos Island")
+    (57 . "Columbia")
+    (269 . "Comoros")
+    (242 . "Congo")
+    (682 . "Cook Islands")
+    (506 . "Costa Rica")
+    (225 . "Cote d'Ivorie")
+    (385 . "Croatia")
+    (53 . "Cuba")
+    (357 . "Cyprus")
+    (42 . "Czech Republic")
+    (45 . "Denmark")
+    (253 . "Djibouti")
+    (593 . "Ecuador")
+    (20 . "Egypt")
+    (503 . "El Salvador")
+    (240 . "Equatorial Guinea")
+    (291 . "Eritrea")
+    (372 . "Estonia")
+    (251 . "Ethiopia")
+    (500 . "Falkland Islands")
+    (298 . "Faroe Islands")
+    (679 . "Fiji")
+    (358 . "Finland")
+    (33 . "France")
+    (594 . "French Guiana")
+    (689 . "French Polynesia")
+    (241 . "Gabon")
+    (220 . "Gambia")
+    (49 . "Germany")
+    (233 . "Ghana")
+    (350 . "Gibraltar")
+    (30 . "Greece")
+    (299 . "Greenland")
+    (590 . "Guadeloupe")
+    (671 . "Guam")
+    (502 . "Guatemala")
+    (245 . "Guinea - Bissau")
+    (224 . "Guinea")
+    (592 . "Guyana")
+    (509 . "Haiti")
+    (504 . "Honduras")
+    (852 . "Hong Kong")
+    (36 . "Hungary")
+    (354 . "Iceland")
+    (91 . "India")
+    (62 . "Indonesia")
+    (98 . "Iran")
+    (964 . "Iraq")
+    (353 . "Ireland Republic of")
+    (972 . "Israel")
+    (39 . "Italy")
+    (225 . "Ivory Coast see Cote d'Ivorie")
+    (81 . "Japan")
+    (962 . "Jordan")
+    (7 . "Kazakhstan")
+    (254 . "Kenya")
+    (7 . "Kirghizstan")
+    (686 . "Kiribati")
+    (850 . "North Korea")
+    (82 . "South Korea")
+    (965 . "Kuwait")
+    (856 . "Laos")
+    (371 . "Latvia")
+    (961 . "Lebanon")
+    (266 . "Lesotho")
+    (231 . "Liberia")
+    (370 . "Lithuania")
+    (352 . "Luxembourg")
+    (218 . "Lybia")
+    (853 . "Macao")
+    (389 . "Macedonia")
+    (261 . "Madagascar")
+    (265 . "Malawi")
+    (60 . "Malaysia")
+    (960 . "Maldives")
+    (223 . "Mali")
+    (356 . "Malta")
+    (692 . "Marshall Islands")
+    (596 . "Martinique")
+    (222 . "Mauritania")
+    (230 . "Mauritius")
+    (269 . "Mayotte")
+    (52 . "Mexico")
+    (691 . "Micronesia")
+    (373 . "Moldovia")
+    (976 . "Mongolia")
+    (212 . "Morocco")
+    (258 . "Mozanbique")
+    (95 . "Myanmar (Burma)")
+    (264 . "Namibia")
+    (977 . "Napal")
+    (674 . "Nauru")
+    (31 . "Netherlands (Holland)")
+    (599 . "Netherlands Antilles")
+    (687 . "New Caledonia")
+    (505 . "Nicaragua")
+    (227 . "Niger")
+    (234 . "Nigeria")
+    (47 . "Norway")
+    (968 . "Oman")
+    (92 . "Pakistan")
+    (507 . "Panama")
+    (675 . "Papua New Guinea")
+    (595 . "Paraguay")
+    (51 . "Peru")
+    (63 . "Philippines")
+    (649 . "Pitcain Island")
+    (48 . "Poland")
+    (361 . "Portugal")
+    (974 . "Qatar")
+    (40 . "Romania")
+    (7 . "Russia")
+    (250 . "Rwanda")
+    (685 . "Samoa (USA)")
+    (685 . "Samoa Western")
+    (378 . "San Marino")
+    (966 . "Saudi Arabia")
+    (221 . "Senegal")
+    (248 . "Seychelles")
+    (232 . "Sierra Leone")
+    (65 . "Singapore")
+    (42 . "Slovakia")
+    (386 . "Slovenia")
+    (677 . "Solom Islands")
+    (252 . "Somalia")
+    (27 . "South Africa")
+    (34 . "Spain")
+    (94 . "Sri Lanka")
+    (290 . "St Helena")
+    (249 . "Sudan")
+    (597 . "Surinam")
+    (268 . "Swaziland")
+    (46 . "Sweden")
+    (41 . "Switzerland")
+    (963 . "Syria")
+    (886 . "Taiwan")
+    (7 . "Tajikistan")
+    (255 . "Tanzania")
+    (66 . "Thailand")
+    (228 . "Togo")
+    (676 . "Tongo")
+    (216 . "Tunisia")
+    (90 . "Turkey")
+    (7 . "Turkmenistan")
+    (688 . "Tuvalu")
+    (1 . "USA")
+    (256 . "Uganda")
+    (380 . "Ukraine")
+    (971 . "United Arab Emirates")
+    (44 . "United Kingdom")
+    (598 . "Uraguay")
+    (7 . "Uzbekistan")
+    (678 . "Vanuatu")
+    (58 . "Venezuela")
+    (84 . "Vietnam")
+    (967 . "Yemen")
+    (381 . "Yugoslavia")
+    (243 . "Zaire")
+    (260 . "Zambia")
+    (263 . "Zimbabwe"))
+  "ISO 3166 country codes.")
+
+(defvar eicq-outgoing-queue nil
+  "Lists of outgoing queue to be sent.
+Each queue consists of the binary string and the resend counter.")
+
+(defun eicq-send (bin &optional count)
+  "Same as `eicq-send-internal' except it uses a queue to slow down sending.
+ICQ v5 server (not v2 server) responses slow and does not acknowledge all
+packets if they are sent too quickly.  Now uses a queue to store all
+outgoing packets, and send them out one by one in short but distinct
+intervals.
+BIN is the string to send.
+COUNT means how many time this packets has been resent. Default is 0."
+  (setq eicq-outgoing-queue
+        (append eicq-outgoing-queue
+                (list (list
+                       (eicq-bin-int bin 16) ; seq-no
+                       bin
+                       (or count 0))))))
+
+(defvar eicq-dropped-packet-counter 0
+  "For debug purpose only.")
+
+(defvar eicq-resend-packet-counter 0
+  "For debug purpose only.")
+
+(defun eicq-send-queue-start ()
+  "Start sending outgoing queue."
+  (eicq-send-queue-stop)
+  (start-itimer
+   "eicq send-queue"
+   (lambda ()
+     (if eicq-outgoing-queue
+         (let ((x (pop eicq-outgoing-queue)))
+           (if (> (third x) 60)         ; exceed resend limit = 5
+               (incf eicq-dropped-packet-counter)
+             (if (zerop (mod (third x) 10))
+                 (if (>= (third x) 10)
+                     (incf eicq-resend-packet-counter))
+                 (eicq-send-internal (second x)))
+             ;; cycle the queue
+             (eicq-send (second x) (1+ (third x)))))))
+   0.5 0.5))
+
+(defun eicq-send-queue-stop ()
+  "Stop sending outgoing queue.."
+  (setq eicq-outgoing-queue nil)
+  (delete-itimer "eicq send-queue"))
+
+;;; Code - network:
+
+(defvar eicq-network nil
+  "TCP network between Emacs and `udp2tcp'.")
+
+(defvar eicq-bridge nil
+  "Process `udp2tcp'.
+It bridges UDP (ICQ server) and TCP (Emacs).")
+
+(defcustom eicq-bridge-filename 
+  "/usr/local/lib/xemacs/xemacs-packages/lib-src/udp2tcp"
+  "*Filename for `eicq-bridge' program."
+  :group 'eicq-option)
+
+(defcustom eicq-bridge-buffer nil
+  "Buffer for `eicq-bridge'.
+Nil means no associated buffer, or no debug info."
+  :group 'eicq-option)
+
+(defcustom eicq-bridge-hostname "127.0.0.1"
+  "*IP address of `eicq-bridge'.
+See `eicq-connect'."
+  :group 'eicq-option)
+
+(defcustom eicq-bridge-port
+  ;; plant random seed
+  (progn (random t) nil)
+  "*Port of `eicq-bridge'.
+See `eicq-connect'."
+  :group 'eicq-option)
+
+(defcustom eicq-server-hostname "icq1.mirabilis.com"
+  "*Hostname or IP address of Mirabilis ICQ server."
+  :group 'eicq-option)
+
+(defcustom eicq-server-port 4000
+  "*Port of Mirabilis ICQ server."
+  :group 'eicq-option)
+
+(defun eicq-bridge-show-buffer ()
+  "Switch to `eicq-bridge-buffer' for network dump info."
+  (interactive)
+  (switch-to-buffer eicq-bridge-buffer))
+
+(defun eicq-connect ()
+  "Make a connection to ICQ server.
+It needs to make `eicq-bridge' and to make `eicq-network'.
+
+A bridge can be running either internally or externally.  If it is running
+external, `eicq-network' will connect to `eicq-bridge-hostname' at
+`eicq-bridge-port'.  If it is running internally, `eicq-bridge-port' should
+be set to nil; then `eicq-bridge-hostname' will be set to \"127.0.0.1\" and
+`eicq-bridge-port' will be assigned a randomly port.
+
+Running externally means no convenient debug network dump inside Emacs, but
+this may allow central bridge servers in future."
+  (unless (or (and (processp eicq-bridge) ; running already
+                   (eq (process-status eicq-network) 'run))
+              eicq-bridge-port)         ; remote bridge
+    (setq eicq-bridge-hostname "127.0.0.1")
+    (setq eicq-bridge-port (+ 4000 (random 1000)))
+    (eicq-log-system
+     "Trying to run a local bridge %s at port %s..."
+     eicq-bridge-hostname eicq-bridge-port)
+    (setq eicq-bridge-buffer (get-buffer-create "*eicq bridge*"))
+    (setq eicq-bridge
+          (start-process
+           "eicq bridge"
+           "*eicq bridge*"
+           eicq-bridge-filename
+           eicq-server-hostname
+           (number-to-string eicq-server-port)
+           (number-to-string eicq-bridge-port)))
+
+    ;; should we implement using filter and accept-process-output?
+
+    (message "Starting up bridge...")
+    ;; wait for `udp2tcp' to execute
+    (accept-process-output eicq-bridge 5)
+    (message "Starting up network...")
+    ;; wait for `udp2tcp' to setup sockets
+    (sleep-for 2))
+
+
+  (unless (eicq-connected-p)
+    (eicq-log-system
+     "Trying to connect to the bridge %s at port %s..."
+     eicq-bridge-hostname eicq-bridge-port)
+    (setq eicq-network
+          (condition-case nil
+              (open-network-stream
+               "eicq network" nil eicq-bridge-hostname eicq-bridge-port)
+            (file-error nil))))         ; eicq-network = nil if fails
+
+  (cond
+   ((eicq-connected-p)
+    (set-process-sentinel eicq-bridge 'eicq-bridge-kill)
+    (set-process-sentinel eicq-network 'eicq-network-kill)
+    (set-process-filter eicq-network 'eicq-network-filter)
+    (with-current-buffer eicq-bridge-buffer
+      (eicq-bridge-mode)))
+   (t
+    (eicq-log-system "....connection failed"))))
+
+(defun eicq-bridge-mode ()
+  "Major mode for bridge debug output.
+Commands: \\{eicq-main-mode}"
+  (make-local-variable 'kill-buffer-hook)
+  (kill-all-local-variables)
+  (use-local-map eicq-main-map)
+  (setq mode-name "eicq-bridge")
+  (setq major-mode 'eicq-bridge-mode)
+  (easy-menu-add eicq-main-easymenu)
+  (make-local-variable 'kill-buffer-query-functions)
+  (add-to-list
+   'kill-buffer-query-functions
+   (lambda ()
+     (if (y-or-n-p "Terminate ICQ? ")
+         (eicq-logout 'kill))))
+  (make-local-variable 'kill-buffer-hook)
+  (add-hook
+   'kill-buffer-hook
+   (lambda () "Kill bridge buffer."
+     (eicq-network-kill)
+     (eicq-bridge-kill))))
+
+(defun eicq-network-kill (&optional process change)
+  "Kill `eicq-network'.
+PROCESS and CHANGE is for `set-process-sentinel'."
+  (if (processp eicq-network) (delete-process eicq-network))
+  (setq eicq-network nil))
+
+(defun eicq-bridge-kill  (&optional process change)
+  "Kill `eicq-network'.
+PROCESS and CHANGE is for `set-process-sentinel'."
+  (if (processp eicq-bridge) (delete-process eicq-bridge))
+  (setq eicq-bridge nil)
+  (if (string= eicq-bridge-hostname "127.0.0.1")
+      (setq eicq-bridge-port nil)))
+
+(defun eicq-disconnect ()
+  "Close `eicq-bridge' and `eicq-network'.
+It does not first logout ICQ server.
+Normally should use `eicq-logout' to logout first."
+  (interactive)
+  (eicq-logout 'kill)
+  (eicq-network-kill)
+  (eicq-bridge-kill))
+
+(defun eicq-connected-p ()
+  "Return non-nil if the network is ready for sending string."
+  (and (processp eicq-network)
+       (eq (process-status eicq-network) 'open)))
+
+(defconst eicq-message-max-size 350
+  "Maximum size of message that ICQ will accept.
+Set it to small because size expands after `eicq-encode-string'.")
+
+(defun eicq-send-internal (bin)
+  "Send a binary string BIN to `eicq-network'.
+`process-send-string' restricts the length of BIN to 500 or less."
+  (if (> (length bin) (+ eicq-message-max-size 50))
+      (eicq-log-error
+       "You try to send a packet of length %s.\n
+It is sent anyway but it may not go through.\n"
+       (length bin)
+       eicq-message-max-size))
+
+  (if (eicq-connected-p)
+      (process-send-string
+       eicq-network
+       (concat (eicq-int-bin (length bin))
+               bin))
+    (eicq-log-error
+     "Network is not connected when it tries to send a packet")
+    (eicq-logout 'kill)))
+
+(defun eicq-network-filter (process bin)
+  "Handle a binary string from `eicq-network'.
+PROCESS is the process which invokes this filter.
+BIN is a binary string from process."
+  (setq eicq-recent-packet bin)
+  (let ((bin-list (eicq-network-separator bin)))
+    (loop for each in bin-list
+      do (eicq-do each))))
+
+(defvar eicq-trimmed-packet nil
+  "*Last incomplete packet.
+Due to limited buffer size of Emacs network buffer, packets can be trimmed
+and attached at the begining of next callback.  Use this in
+`eicq-network-separator' to concatenate a packet arcoss two callbacks.
+Usually only one per 1000 packets needs this.")
+
+(defvar eicq-trimmed-packet-counter 0
+  "For debug purpose only.")
+
+(defun eicq-network-separator (bin)
+  "Separate multiple packets BIN into a list of packet.
+See `eicq-network-filter'."
+  ;; there can be as many as 15 packets or as less as 1 in bin
+  (let (len packets)
+    (setq bin (concat eicq-trimmed-packet bin))
+    (while (not (zerop (length bin)))
+      (setq len (eicq-bin-int bin))
+      (cond
+       ((> (+ 2 len) (length bin))
+        ;; last trimmed packet
+        (incf eicq-trimmed-packet-counter)
+        (setq eicq-trimmed-packet bin)
+        (setq bin nil))                 ; to quit while loop
+       (t
+        (push (substring bin 2 (+ 2 len)) packets)
+        (setq bin (substring bin (+ 2 len)))
+        (if (zerop (length bin))
+            (setq eicq-trimmed-packet nil)))))
+    packets))
+
+(defvar eicq-recent-packet nil
+  "The most recent incoming packet.
+For debug only.")
+
+(defvar eicq-error-packets nil
+  "A list of error incoming packets.
+For debug only.")
+
+;;; Code - client to server packets:
+
+(defvar eicq-session-id
+  (concat
+   (eicq-int-bin (random 65535))
+   (eicq-int-bin (random 65535)))
+  "Random number used by ICQ client to anti-spoof.")
+
+(defvar eicq-current-seq-num 1
+  "Current sequence number in packet.")
+
+(defun eicq-pack (command &rest parameters)
+  "Pack a ICQ protocol version 5 packet.
+COMMAND is a hex string.
+PARAMETERS is a binary string.
+Return a binary string.
+Use `eicq-send' to send the string."
+  (concat "\x05\x00"
+          "\x00\x00\x00\x00"
+          eicq-user-bin
+          eicq-session-id
+          command
+          (eicq-int-bin (incf eicq-current-seq-num))
+          (eicq-int-bin eicq-current-seq-num)
+          "\x00\x00\x00\x00"            ; checkcode
+          (if parameters
+              (apply 'concat parameters)
+            "\x00\x00\x00\x00")))       ; must have at least 4 bytes
+
+(defun eicq-pack-login ()
+  "Pack login packet 03e8."
+  (let* ((password (or eicq-user-password
+                       (read-passwd "password: ")))
+         (password-len (eicq-int-bin (1+ (length password))))
+         (status (eicq-status-bin eicq-user-initial-status)))
+    (eicq-pack
+     "\xe8\x03"
+     ;; "\x5f\x71\x69\x37"                      ; time
+     "\x00\x00\x00\x00"                 ; time
+     "\x00\x00\x00\x00"                 ; port (auto-detect)
+     password-len
+     password "\x00"
+     "\x72\x00\x04\x00"                 ; x1
+     "\x00\x00\x00\x00"                 ; ip (auto-detect)
+     "\x06"                             ; x2 _d95_
+     status                             ; initial status
+     ;; FIXME invisible has 2 bytes while others only one
+     ;; must be \x00 so that initial status works
+     "\x00\x00\x00\x00"                 ; x3 _d95_
+     "\x02\x00"                         ; seq num, WHY 2?
+     "\x00\x00\x00\x00"                 ; x4
+     "\x04\x00\x72\x00")))
+
+(defun eicq-pack-keep-alive ()
+  "Pack keep alive packet 042e."
+  (eicq-pack "\x2e\x04"))
+
+(defun eicq-pack-keep-alive-1 ()
+  "Pack another keep alive packet 051e.
+Obseleted. Not used in v5."
+  (eicq-pack "\x1e\x05"))
+
+(defun eicq-pack-logout ()
+  "Pack logout packet using 0438."
+  ;; also used to send text code to server
+  (eicq-pack
+   "\x38\x04"
+   "\x14\x00B_USER_DISCONNECTED\x00\x05\x00"))
+
+(defun eicq-pack-delete-offline-messages ()
+  "Pack delete offline message packet 0442."
+  (eicq-pack "\x42\x04"))
+
+(defun eicq-pack-contact-list ()
+  "Pack contact list packet 0406."
+  ;; It first makes a large binary string of all uin, then chops into
+  ;; chuncks and send them separately with `eicq-send-contact-list'.
+  ;; each uin = 4, 4 * max 50 = 200 bytes, to avoid large packets
+  ;; Packets < 400 can go through but not readily acknowledged (need
+  ;; resending). Small-sized packets are good.
+  (let* ((all-uin-bin  (mapconcat 'eicq-uin-bin eicq-all-uin nil))
+         (n (length all-uin-bin))
+         (i 0)
+         packets)
+    (while (< i n)
+      (let* ((bin (substring all-uin-bin i (min (incf i 200) n)))
+             ;; since server does not do bound check,
+             ;; wrong count will pop up with random uin
+             (count (/ (length bin) 4)))
+        (push
+         (eicq-pack "\x06\x04" (eicq-int-byte count) bin)
+         packets)))
+    packets))
+
+(defun eicq-pack-invisible-list ()      ; TODO
+  "Pack visible list packet 06a4."
+  ;; format same as eicq-pack-contact-list
+  )
+
+(defun eicq-pack-visible-list ()        ; TODO
+  "Pack visible list packet 06ae."
+  ;; format same as eicq-pack-contact-list
+  )
+
+(defvar eicq-message-types
+  '(("\x01" . normal)
+    ("\x02" . chat-request)
+    ("\x04" . url)
+    ("\x06" . request-auth)
+    ("\x08" . authorize)
+    ("\x0b" . request-auth)
+    ("\x0c" . added)
+    ("\x0d" . pager)
+    ("\x0e" . email-express)
+    ("\x0f" . email-check)
+    ("\x1a" . card)
+    ("\x13" . contact-list))
+  "See `eicq-pack-send-message'.")
+
+(defun eicq-pack-send-message (alias message type)
+  "Pack send message of any kinds packet 010e.
+ALIAS is the alias/uin to send to.
+MESSAGE is the message body.
+TYPE is the message type.
+Possible TYPE: `eicq-send-message-types'."
+  (eicq-pack
+   "\x0e\x01"
+   (eicq-alias-bin alias)
+   (car (rassoc type eicq-message-types))
+   "\x00"
+   (eicq-int-bin (1+ (length message)))
+   message "\x00"))
+
+(defun eicq-pack-status-change (status)
+  "Pack STATUS change packet 04d8."
+  (eicq-pack
+   "\xd8\x04"
+   (eicq-status-bin status)))
+
+(defun eicq-pack-register-new-user (password)
+  "Pack register new user packet 03fc."
+  (eicq-pack
+   "\xfc\x03"
+   (eicq-int-bin (length password))
+   password
+   "\xa0\x00\x00\x00"
+   "\x24\x61\x00\x00"
+   "\x00\x00\x00\x00"))
+
+(defun eicq-pack-meta-user-change-password (password)
+  "Pack meta user change password packet 064a:042e.
+ALIAS is the alias/uin to query info for."
+  (eicq-pack
+   "\x4a\x06"
+   "\x2e\x04"
+   (eicq-int-bin (length password))
+   password))
+
+(defun eicq-pack-meta-user-query (alias)
+  "Pack meta user query packet 064a:04b0.