changeset 364:33b8e801ffa6

Merge changeset 315 (65aa05520556)
author Mikael Berthe <mikael@lilotux.net>
date Sun, 24 Jul 2005 15:13:53 +0100
parents 913915140ad2 (diff) 65aa05520556 (current diff)
children ddb6593bedc9
files mcabber/NEWS mcabber/README mcabber/configure.ac mcabber/doc/mcabber.1 mcabber/doc/mcabber.1.html mcabber/doc/mcabber.1.txt mcabber/libjabber/xmltok_impl_c.h mcabber/mcabberrc.example mcabber/src/Makefile.am mcabber/src/commands.c mcabber/src/hbuf.h mcabber/src/histolog.c mcabber/src/hooks.c mcabber/src/hooks.h mcabber/src/jabglue.c mcabber/src/main.c mcabber/src/roster.c mcabber/src/roster.h mcabber/src/screen.c mcabber/src/screen.h mcabber/src/settings.c mcabber/src/settings.h mcabber/src/utf8.c mcabber/src/utils.c mcabber/src/utils.h
diffstat 41 files changed, 958 insertions(+), 644 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri Jul 15 10:56:15 2005 +0100
+++ b/.hgtags	Sun Jul 24 15:13:53 2005 +0100
@@ -2,3 +2,4 @@
 ff9ba796cabba5547b33b096ac48027cb5cc2ccd 0.6.1
 bb71b5707b00e6a8cd028e350a33c455692723a5 0.6.2
 70914672c8e71ee0e99ecb1f45800216ec9efebe 0.6.3
+74cf831b8117f39465d10fbe2bf766a46d60955a 0.6.4
--- a/mcabber/ChangeLog	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/ChangeLog	Sun Jul 24 15:13:53 2005 +0100
@@ -1,10 +1,27 @@
-mcabber (0.6.4-dev)
+mcabber (0.6.5-dev)
 
- * One Ctrl-C does not terminate mcabber anymore (the 2nd Ctrl-C do), but
-   abort current completion
+ 
 
  -- Mikael, ?
 
+mcabber (0.6.4)
+
+ * Configuration file format change (see NEWS file)
+   Aliases & key bindings can be put in the config. file
+ * Enable /set command
+ * [FIX] Convert status messages to/from UTF-8
+ * The /status command can specify a status message
+ * Display the buddy status message when a buddy connects/changes his status
+ * New autoaway feature (see sample config. file)
+ * New "/roster alternate".  Jumps to the last buddy window left in chat mode
+ * Handle "error" message type
+ * One Ctrl-C does not terminate mcabber anymore (the 2nd Ctrl-C do), but
+   leaves multi-line message mode and aborts current completion
+ * Add a sample script to handle events (currently, it plays a sounds when
+   a message is received)
+
+ -- Mikael, 2005-07-20
+
 mcabber (0.6.3)
 
  * Fix interactive password crash
--- a/mcabber/NEWS	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/NEWS	Sun Jul 24 15:13:53 2005 +0100
@@ -1,3 +1,10 @@
+mcabber (0.6.4)
+
+ * The configuration file format has changed.  Options need to be set using
+   the "set" keywords.  The sample config. file has been updated.
+
+ -- Mikael, 2005-07-18
+
 mcabber (0.6.2)
 
  * Support for old style (i.e. < 0.6.1) history logfiles has been removed.
--- a/mcabber/README	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/README	Sun Jul 24 15:13:53 2005 +0100
@@ -1,12 +1,15 @@
-Well, the documentation is missing yet, but...
+ Hello, and thanks for trying mcabber.
 
-!!!!!! SEE mcabberrc.example !!!!!!
+For installation instruction, have a look at the INSTALL file.
 
-A configuration file is necessary.
-Its name is $HOME/.mcabber/mcabberrc (or $HOME/.mcabberrc).
-Please see the sample config file!
+A configuration file is necessary to start mcabber.  Its name is
+$HOME/.mcabber/mcabberrc (or $HOME/.mcabberrc).
 
-You can use the -f option to specify another configuration file.
+A sample configuration file, "mcabberrc.example", is provided in this
+directory.  Copy it and modify it to fit your needs.
+
+
+You can use mcabber "-f" option to specify another configuration file.
 
 
 This software is under development, please give me some feedback (and some
--- a/mcabber/TODO	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/TODO	Sun Jul 24 15:13:53 2005 +0100
@@ -5,7 +5,10 @@
 
 TODO:
 
+* More events
+* Add debian/ directory
 * Improve debug logging system
+* Implement automatic reconnection after network failure
 * Presence notification is always accepted.  We should ask...
 * UTF-8 support (Can somebody help me?)
 * Display status / chat mode
@@ -17,13 +20,9 @@
 * Create .mcabber and .mcabber/histo dirs if needed.  Or maybe not.
   However it could be a good idea to check the permissions.
 * Publish personal information
-* Handle message type "error"
 * Show status changes in buddy window (if open)?
 * Options completion
 * Configuration file parsing: parse as commands (set, alias, bind)
-* Auto away
-* Add a sample script for the events command
-* Make first Ctrl-C leave multi-line mode
 * Keep track of buddy resources.  Ex.:
   - buddy A connects with resource r1
   - buddy A connects with resource r2
@@ -49,7 +48,6 @@
   - /auth request|send [jid]
   - /search <jid>|name
     (server search)
-  - status status Message
   - /help
   - /rawxml...
 
--- a/mcabber/configure.ac	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/configure.ac	Sun Jul 24 15:13:53 2005 +0100
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT([mcabber],[0.6.4-dev],[mcabber@lilotux.net])
+AC_INIT([mcabber],[0.6.5-dev],[mcabber@lilotux.net])
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([src])
 AM_CONFIG_HEADER(config.h)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/contrib/eventcmd	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,33 @@
+#! /bin/sh
+#
+# Sample events script for mcabber
+# Plays a sound when receiving a message
+#
+# To use this script, set the "events_command" option to the path of
+# the script (see the mcabberrc.example file for an example)
+#
+# MiKael, 2005-07-15
+
+# The following sound comes with the gtkboard package,
+# you can modify this line to play another one...
+CMD_MSG_IN="/usr/bin/play /usr/share/sounds/gtkboard/machine_move.ogg"
+
+event=$1
+arg1=$2
+arg2=$3
+
+if [ $event == "MSG" ]; then
+  case "$arg1" in
+    IN)
+      # Incoming message from buddy $arg2
+      $CMD_MSG_IN > /dev/null 2>&1
+      ;;
+    OUT)
+      # Outgoing message for buddy $arg2
+      ;;
+  esac
+elif [ $event == "STATUS" ]; then
+  # Buddy $arg2 status is $arg1 (_, O, I, F, D, N, A)
+  echo > /dev/null
+fi
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/changelog	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,12 @@
+mcabber (0.6.4-2) unstable; urgency=low
+
+  * Improve Debian package, with some help from Benjamin Drieu
+
+ -- Mikael BERTHE <mikael.berthe@free.fr>  Sat, 23 Jul 2005 15:26:58 +0200
+
+mcabber (0.6.4-1) unstable; urgency=low
+
+  * Initial release
+
+ -- Mikael BERTHE <mikael.berthe@lilotux.net>  Thu, 21 Jul 2005 20:32:41 +0200
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/compat	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,1 @@
+4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/control	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,16 @@
+Source: mcabber
+Section: net
+Priority: optional
+Maintainer: Mikael BERTHE <mikael.berthe@lilotux.net>
+Build-Depends: debhelper (>= 4.0.0), autotools-dev, libglib2.0-dev, libncurses5-dev, libssl-dev
+Standards-Version: 3.6.1
+
+Package: mcabber
+Section: net
+Priority: optional
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: Small Jabber console client
+ MCabber is a Jabber text-mode client, which includes features such as
+ SSL support, history logging, commands completion, and external actions
+ triggers.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/copyright	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,27 @@
+This package was debianized by Mikael BERTHE <mikael.berthe@lilotux.net> on
+Thu, 21 Jul 2005 20:32:41 +0200.
+
+It was downloaded from: http://www.lilotux.net/~mikael/mcabber/
+
+Copyright Holder: Mikael BERTHE <mikael.berthe@lilotux.net>
+
+License:
+
+   This package is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This package is distributed in the hope that it will be useful,
+   but WITHOUT 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 package; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/docs	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,6 @@
+AUTHORS
+NEWS
+README
+TODO
+doc/mcabber.1.txt
+doc/mcabber.1.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/mcabber.doc-base	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,15 @@
+Document: mcabber
+Title: Debian mcabber Manual
+Author: Mikael BERTHE
+Abstract: This manual describes what mcabber is
+ and how it can be used.
+Section: Apps/Net
+
+#Format: text
+#Files: /usr/share/doc/mcabber/mcabber.1.txt.gz
+
+Format: HTML
+Index: /usr/share/doc/mcabber/mcabber.1.html
+Files: /usr/share/doc/mcabber/mcabber.1.html
+
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/menu	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,2 @@
+?package(mcabber):needs="text" section="Apps/Net"\
+  title="MCabber" command="/usr/bin/mcabber"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/rules	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,107 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+	CFLAGS += -O0
+else
+	CFLAGS += -O2
+endif
+
+config.status: configure
+	dh_testdir
+	# Add here commands to configure the package.
+	CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info
+
+
+build: build-stamp
+
+build-stamp:  config.status
+	dh_testdir
+
+	# Add here commands to compile the package.
+	$(MAKE)
+	#docbook-to-man debian/mcabber.sgml > mcabber.1
+
+	touch build-stamp
+
+clean:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp 
+
+	# Add here commands to clean up after the build process.
+	-$(MAKE) distclean
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+	cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+	cp -f /usr/share/misc/config.guess config.guess
+endif
+
+
+	dh_clean 
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k 
+	dh_installdirs
+
+	# Add here commands to install the package into debian/mcabber.
+	$(MAKE) install DESTDIR=$(CURDIR)/debian/mcabber
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir
+	dh_testroot
+	dh_installchangelogs ChangeLog
+	dh_installdocs
+	dh_installexamples mcabberrc.example
+#	dh_install
+#	dh_installmenu
+#	dh_installdebconf	
+#	dh_installlogrotate
+#	dh_installemacsen
+#	dh_installpam
+#	dh_installmime
+#	dh_installinit
+#	dh_installcron
+#	dh_installinfo
+	dh_installman
+	dh_link
+	dh_strip
+	dh_compress
+	dh_fixperms
+#	dh_perl
+#	dh_python
+#	dh_makeshlibs
+	dh_installdeb
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/debian/watch	Sun Jul 24 15:13:53 2005 +0100
@@ -0,0 +1,24 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 3 file
+version=3
+
+# Uncomment to examine a Webpage 
+# <Webpage URL> <string match>
+#http://www.example.com/downloads.php mcabber-(.*)\.tar\.gz
+
+# Uncomment to examine a Webserver directory
+#http://www.example.com/pub/mcabber-(.*)\.tar\.gz
+http://www.lilotux.net/~mikael/mcabber/files/mcabber-(.*)\.tar\.gz
+http://www.lilotux.net/~mikael/mcabber/files/mcabber-(.*)\.tar\.bz2
+
+# Uncommment to examine a FTP server
+#ftp://ftp.example.com/pub/mcabber-(.*)\.tar\.gz debian uupdate
+
+# Uncomment to use Roland's hack for sourceforge based projects - YMMV!
+#http://people.debian.org/~lolando/sfdlr.php?project=mcabber mcabber-([\d.]*).tar.gz
+
+
--- a/mcabber/doc/mcabber.1	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/doc/mcabber.1	Sun Jul 24 15:13:53 2005 +0100
@@ -31,6 +31,9 @@
 mcabber(1) is a small Jabber console client\&. For now it needs a configuration file to start, so please copy the sample mcabberrc file and adapt your connection settings\&.
 
 
+You also need to have an existing Jabber account to use this software, as it cannot (un)register accounts yet\&.
+
+
 The mcabber(1) screen is divided into 4 regions\&. The \fIroster\fR, alias \fIbuddylist\fR, is on the left\&. The \fIchat window\fR, or chat buffer, is on the right\&. The \fIinput line\fR lies at the bottom of the screen, under a small \fIlog window\fR\&.
 
 
@@ -180,6 +183,7 @@
  \fBshow_offline\fR	show offline buddies
  \fBtoggle_offline\fR	toggle display of offline buddies
  \fBsearch\fR bud	search for a buddy with a name or buddy containing "bud" (only in the displayed buddylist)
+ \fBalternate\fR	jump to alternate buddy\&. The "alternate" buddy is the last buddy left while being in chat mode (this command is thus especially useful after commands like "/roster unread_first")
  \fBunread_first\fR	jump to the first unread message
  \fBunread_next\fR	jump to the next unread message
 
@@ -188,8 +192,8 @@
 Send the text message to the currently selected buddy\&. Can be useful if you want to send a message beginning with a slash, for example\&.
 
 .TP
-\fB/status\fR [online|avail|invisible|free|dnd|notavail|away]
-Set the current status\&. If no status is specified, display the current status\&.
+\fB/status\fR [online|avail|invisible|free|dnd|notavail|away [StatusMessage]]
+Set the current status\&. If no status is specified, display the current status\&. If a status message is specified, it will overrride the message* variables\&.
 
 .SH "CONFIGURATION FILE"
 
@@ -212,7 +216,7 @@
 .SH "BUGS"
 
 
-Certainly a lot\&. Please tell me if you find one! :\-)
+Certainly\&. Please tell me if you find one! :\-)
 
 .SH "AUTHOR"
 
--- a/mcabber/doc/mcabber.1.html	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/doc/mcabber.1.html	Sun Jul 24 15:13:53 2005 +0100
@@ -23,6 +23,8 @@
 <p><tt>mcabber(1)</tt> is a small Jabber console client.<br />
 For now it needs a configuration file to start, so please copy the sample
 mcabberrc file and adapt your connection settings.</p>
+<p>You also need to have an existing Jabber account to use this software, as
+it cannot (un)register accounts yet.</p>
 <p>The <tt>mcabber(1)</tt> screen is divided into 4 regions.
 The <b>roster</b>, alias <b>buddylist</b>, is on the left.  The <b>chat window</b>, or chat
 buffer, is on the right.  The <b>input line</b> lies at the bottom of the screen,
@@ -390,7 +392,10 @@
         the buddies to another group with the /move command).
 </dd>
 <dt><b>
-/roster bottom|top|hide_offline|show_offline|toggle_offline|unread_first|unread_next
+/roster bottom|top|hide_offline|show_offline|toggle_offline
+</b></dt>
+<dt><b>
+/roster alternate|unread_first|unread_next
 </b></dt>
 <dt><b>
 /roster search bud
@@ -448,6 +453,14 @@
 </tr>
 <tr valign="top">
 <td>
+<b>alternate</b>
+</td>
+<td>
+jump to alternate buddy.  The "alternate" buddy is the last buddy left while being in chat mode (this command is thus especially useful after commands like "/roster unread_first")
+</td>
+</tr>
+<tr valign="top">
+<td>
 <b>unread_first</b>
 </td>
 <td>
@@ -472,11 +485,13 @@
         if you want to send a message beginning with a slash, for example.
 </dd>
 <dt><b>
-/status [online|avail|invisible|free|dnd|notavail|away]
+/status [online|avail|invisible|free|dnd|notavail|away [StatusMessage]]
 </b></dt>
 <dd>
         Set the current status.  If no status is specified, display the
-        current status.
+        current status.<br />
+        If a status message is specified, it will overrride the message*
+        variables.
 </dd>
 </dl>
 <h2>CONFIGURATION FILE</h2>
@@ -487,7 +502,7 @@
 $HOME/.mcabberrc            Configuration file used if no other has been found
 $HOME/.mcabber/histo/       Default directory for storing chat history files, if enabled</pre></div>
 <h2>BUGS</h2>
-<p>Certainly a lot.  Please tell me if you find one!  :-)</p>
+<p>Certainly.  Please tell me if you find one!  :-)</p>
 <h2>AUTHOR</h2>
 <p>Written by <a href="mailto:mcabber@lilotux.net">Mikael BERTHE</a>.<br />
 Originally based on <a href="http://cabber.sourceforge.net">Cabber</a>, please
@@ -501,8 +516,8 @@
 License (GPL).</p>
 <div id="footer">
 <p>
-Version 0.6.3<br />
-Last updated 10-Jul-2005 16:33:35 CEST
+Version 0.6.5-dev<br />
+Last updated 23-Jul-2005 16:09:54 CEST
 </p>
 </div>
 </div>
--- a/mcabber/doc/mcabber.1.txt	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/doc/mcabber.1.txt	Sun Jul 24 15:13:53 2005 +0100
@@ -1,7 +1,7 @@
 MCABBER(1)
 ===========
 Mikael BERTHE <mcabber@lilotux.net>
-v0.6.4-dev, July 2005
+v0.6.5-dev, July 2005
 
 NAME
 ----
@@ -17,6 +17,9 @@
 For now it needs a configuration file to start, so please copy the sample
 mcabberrc file and adapt your connection settings.
 
+You also need to have an existing Jabber account to use this software, as
+it cannot (un)register accounts yet.
+
 The `mcabber(1)` screen is divided into 4 regions.
 The 'roster', alias 'buddylist', is on the left.  The 'chat window', or chat
 buffer, is on the right.  The 'input line' lies at the bottom of the screen,
@@ -162,7 +165,8 @@
         This command does not work for groups, at the moment (but you can move
         the buddies to another group with the /move command).
 
-/roster bottom|top|hide_offline|show_offline|toggle_offline|unread_first|unread_next::
+/roster bottom|top|hide_offline|show_offline|toggle_offline::
+/roster alternate|unread_first|unread_next::
 /roster search bud::
         The 'roster' command manipulates the roster/buddylist.  Here are the available parameters:
 
@@ -172,6 +176,7 @@
         'show_offline';; show offline buddies
         'toggle_offline';; toggle display of offline buddies
         'search' bud;;   search for a buddy with a name or buddy containing "bud" (only in the displayed buddylist)
+        'alternate';;    jump to alternate buddy.  The "alternate" buddy is the last buddy left while being in chat mode (this command is thus especially useful after commands like "/roster unread_first")
         'unread_first';; jump to the first unread message
         'unread_next';;  jump to the next unread message
 
@@ -179,9 +184,11 @@
         Send the text message to the currently selected buddy.  Can be useful
         if you want to send a message beginning with a slash, for example.
 
-/status [online|avail|invisible|free|dnd|notavail|away]::
+/status [online|avail|invisible|free|dnd|notavail|away [StatusMessage]]::
         Set the current status.  If no status is specified, display the
-        current status.
+        current status. +
+        If a status message is specified, it will overrride the message*
+        variables.
 
 CONFIGURATION FILE
 ------------------
@@ -197,7 +204,7 @@
 
 BUGS
 ----
-Certainly a lot.  Please tell me if you find one!  :-)
+Certainly.  Please tell me if you find one!  :-)
 
 AUTHOR
 ------
--- a/mcabber/libjabber/xmltok_impl_c.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/libjabber/xmltok_impl_c.h	Sun Jul 24 15:13:53 2005 +0100
@@ -1391,7 +1391,7 @@
 {
     enum { other, inName, inValue } state = inName;
     int nAtts = 0;
-    int open;
+    int open = 0;
 
     for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
         switch (BYTE_TYPE(enc, ptr)) {
--- a/mcabber/mcabberrc.example	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/mcabberrc.example	Sun Jul 24 15:13:53 2005 +0100
@@ -5,61 +5,85 @@
 # If port is not given, default Jabber port will be used.
 # Use ssl = 1 to enable SSL
 
-username = yourusername
-#password = yourpassword
-server = your.jabber.server
-#port = 5222
-ssl = 0
+set username = yourusername
+# Note: if the password contains leading or trailing spaces, you must
+# enclose it with quotes: set password = " example password "
+#set password = yourpassword
+set server = your.jabber.server
+#set port = 5222
+set ssl = 0
 # If you don't know what a resource is, you can leave "mcabber" here.
-resource = mcabber
-#priority = 3
+set resource = mcabber
+#set priority = 3
 
 # Keepalive
 # If you need a ping/keepalive to leave your connection open, you
 # can use the pinginterval.  Setting this option to 0 disables the ping.
 # Default value is 40 seconds.
-#pinginterval = 40
+#set pinginterval = 40
 
 # Set hide_offline_buddies to 1 to display only connected buddies
 # in the roster.
-#hide_offline_buddies = 0
+#set hide_offline_buddies = 0
+
+# Set the auto-away timeout, in seconds.  If set to a value >0,
+# mcabber will change your status to away if no real activity is detected
+# (command, message, move in the buddylist...).  Note: auto-away only changes
+# the status when it is "available" (online) or "free_for_chat".
+#set autoaway = 0
 
 # History logging
 # You can save the messages history: set logging = 1
 # You can load (read) the messages history: set load_logs = 1
 # Default logging directory (logging_dir) is $HOME/.mcabber/histo/
-logging = 1
-#load_logs = 1
-#logging_dir = /home/mikael/.mcabber/histo/
+# Defaults for logging, load_logs are 0 (disabled)
+# Note: and the logging directory must exist if you enable logging.
+#set logging = 1
+#set load_logs = 1
+#set logging_dir = /home/mikael/.mcabber/histo/
 
 # External command for events
 # You can specify a script or process to be launched when an event occurs.
-# For now it is called the following way:
-#   $events_command MSG IN jabber@id
-# ... when receiving a message.
-#events_command = /home/mikael/.mcabber/eventcmd
+# The command is called the following way:
+#   $events_command MSG IN jabber@id        (when receiving a message)
+#   $events_command MSG OUT jabber@id       (when sending a message)
+#   $events_command STATUS X jabber@id      (new buddy status is X)
+# See sample script in contrib/ directory.
+#set events_command = /home/mikael/.mcabber/eventcmd
 
 # Debug logging
 # If you want advanced debug, please specify a file here.
 # You can enable debug in main.c before compiling mcabber, too.
-#debug = /home/mikael/mcabber.log
+#set debug = /home/mikael/mcabber.log
 
 #  Status messages
 # The "message" value will override all others, take care!
-#message = Unique message status
-#message_avail     = I'm available
-#message_free      = I'm free for chat
-#message_dnd       = Please do not disturb
-#message_notavail  = I'm not available
-#message_away      = I'm away
-#message_autoaway  = Auto-away (Not yet implemented)
+#set message = Unique message status
+#set message_avail     = I'm available
+#set message_free      = I'm free for chat
+#set message_dnd       = Please do not disturb
+#set message_notavail  = I'm not available
+#set message_away      = I'm away
+#set message_autoaway  = Auto-away
 
 #  The colors
 # Colors are: black, red, green, yellow, blue, magenta, cyan, white
-#color_background   = blue
-#color_general      = white
-#color_newmessage   = red
-#color_rosternormal = magenta
-#color_rosterselect = black
-#color_backselected = cyan
+#set color_background   = blue
+#set color_general      = white
+#set color_newmessage   = red
+#set color_rosternormal = magenta
+#set color_rosterselect = black
+#set color_backselected = cyan
 
+#  Aliases
+alias online   = status online
+alias away     = status away
+alias dnd      = status dnd
+alias notavail = status notavail
+
+#  Key bindings
+# Ctlr-X (24) bound to /roster alternate
+bind 24 = roster alternate
+# F5 (269) bound to /roster toggle_offline  (centericq-like, IIRC)
+bind 269 = roster toggle_offline
+
--- a/mcabber/src/Makefile.am	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/Makefile.am	Sun Jul 24 15:13:53 2005 +0100
@@ -2,7 +2,7 @@
 mcabber_SOURCES = main.c jabglue.c jabglue.h roster.c roster.h \
 		  commands.c commands.h compl.c compl.h \
 		  hbuf.c hbuf.h screen.c screen.h \
-		  settings.c settings.h parsecfg.c parsecfg.h \
+		  settings.c settings.h \
 		  hooks.c hooks.h histolog.c histolog.h \
 		  utf8.c utf8.h utils.c utils.h list.h harddefines.h
 
--- a/mcabber/src/Makefile.mcabber	Fri Jul 15 10:56:15 2005 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-#
-# mcabber Makefile
-#
-
-
-#
-#  Available options:
-#
-#    Environment variables:
-#	CPU		optimize for the given processor.
-#			default = pentium
-#	DEBUG=1		disable optimizations and build for debug
-#			default = no
-#	GNU99=1		use GNU99 extensions
-#			default = no
-#
-#    Targets:
-#	all:		build executable
-#	clean:		remove object files
-#	realclean:	remove all generated files
-#	install:	build & install
-#	dep:		create dependencies
-#
-
-
-.PHONY: all clean realclean install
-# dep
-
-CPU ?= i386
-JCLIENT = mcabber
-ifndef CC
-CC = gcc
-endif
-CFLAGS = -Wall -W -pedantic
-LD = $(CC)
-LDLIBS = -lncurses -lpanel -lssl -L../libjabber -L../connwrap -llibjabber -lconnwrap
-
-ifeq ($(DEBUG),1)
-CFLAGS += -O0 -g -DDEBUG=1
-else
-CFLAGS += -O2 -mcpu=$(CPU)
-LDFLAGS = -s
-endif
-
-ifeq ($(GNU99),1)
-CFLAGS += -std=gnu99 -D_GNU_SOURCE
-endif
-
-CP = cp -f
-
-SOURCES = \
-    main.c \
-    commands.c \
-    screen.c \
-    utils.c \
-    buddies.c \
-    parsecfg.c \
-    jabglue.c \
-    lang.c \
-    utf8.c
-
-OBJECTS = $(SOURCES:.c=.o)
-
-.c.o:
-	$(CC) -o $@ $(CFLAGS) -c $<
-
-all: $(JCLIENT)
-
-$(JCLIENT): $(OBJECTS)
-	$(LD) -o $@ $(LDFLAGS) $^ $(LDLIBS)
-
-clean:
-	-$(RM) *~
-	-$(RM) $(JCLIENT)
-	-$(RM) $(OBJECTS)
-#	-$(RM) depend
-
-realclean: clean
-	-$(RM) $(JCLIENT)
-
-install: all
-	$(CP) $(JCLIENT) /usr/local/bin/$(JCLIENT)
-
-#dep: $(SOURCES)
-#	makedepend -f- -Ylydialog -- $(CFLAGS) -- $(SOURCES) > depend
-
-#-include depend
--- a/mcabber/src/commands.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/commands.c	Sun Jul 24 15:13:53 2005 +0100
@@ -33,23 +33,23 @@
 #include "settings.h"
 
 // Commands callbacks
-void do_roster(char *arg);
-void do_status(char *arg);
-void do_add(char *arg);
-void do_del(char *arg);
-void do_group(char *arg);
-void do_say(char *arg);
-void do_msay(char *arg);
-void do_buffer(char *arg);
-void do_clear(char *arg);
-void do_info(char *arg);
-void do_rename(char *arg);
-void do_move(char *arg);
-void do_set(char *arg);
-void do_alias(char *arg);
-void do_bind(char *arg);
-void do_connect(char *arg);
-void do_disconnect(char *arg);
+static void do_roster(char *arg);
+static void do_status(char *arg);
+static void do_add(char *arg);
+static void do_del(char *arg);
+static void do_group(char *arg);
+static void do_say(char *arg);
+static void do_msay(char *arg);
+static void do_buffer(char *arg);
+static void do_clear(char *arg);
+static void do_info(char *arg);
+static void do_rename(char *arg);
+static void do_move(char *arg);
+static void do_set(char *arg);
+static void do_alias(char *arg);
+static void do_bind(char *arg);
+static void do_connect(char *arg);
+static void do_disconnect(char *arg);
 
 // Global variable for the commands list
 static GSList *Commands;
@@ -118,6 +118,7 @@
   compl_add_category_word(COMPL_ROSTER, "hide_offline");
   compl_add_category_word(COMPL_ROSTER, "show_offline");
   compl_add_category_word(COMPL_ROSTER, "toggle_offline");
+  compl_add_category_word(COMPL_ROSTER, "alternate");
   compl_add_category_word(COMPL_ROSTER, "search");
   compl_add_category_word(COMPL_ROSTER, "unread_first");
   compl_add_category_word(COMPL_ROSTER, "unread_next");
@@ -322,7 +323,7 @@
 
 /* Commands callback functions */
 
-void do_roster(char *arg)
+static void do_roster(char *arg)
 {
   if (!strcasecmp(arg, "top")) {
     scr_RosterTop();
@@ -347,6 +348,8 @@
     scr_RosterUnreadMessage(0);
   } else if (!strcasecmp(arg, "unread_next")) {
     scr_RosterUnreadMessage(1);
+  } else if (!strcasecmp(arg, "alternate")) {
+    scr_RosterJumpAlternate();
   } else if (!strncasecmp(arg, "search", 6)) {
     char *string = arg+6;
     if (*string && (*string != ' ')) {
@@ -365,33 +368,46 @@
     scr_LogPrint("Unrecognized parameter!");
 }
 
-void do_status(char *arg)
+static void do_status(char *arg)
 {
   enum imstatus st;
+  int len;
+  char *msg;
 
   if (!arg || (*arg == 0)) {
     scr_LogPrint("Your status is: %c", imstatus2char[jb_getstatus()]);
     return;
   }
 
-  if (!strcasecmp(arg, "offline"))        st = offline;
-  else if (!strcasecmp(arg, "online"))    st = available;
-  else if (!strcasecmp(arg, "avail"))     st = available;
-  else if (!strcasecmp(arg, "away"))      st = away;
-  else if (!strcasecmp(arg, "invisible")) st = invisible;
-  else if (!strcasecmp(arg, "dnd"))       st = dontdisturb;
-  else if (!strcasecmp(arg, "notavail"))  st = notavail;
-  else if (!strcasecmp(arg, "free"))      st = freeforchat;
+  msg = strchr(arg, ' ');
+  if (!msg)
+    len = strlen(arg);
+  else
+    len = msg - arg;
+
+  if      (!strncasecmp(arg, "offline",   len)) st = offline;
+  else if (!strncasecmp(arg, "online",    len)) st = available;
+  else if (!strncasecmp(arg, "avail",     len)) st = available;
+  else if (!strncasecmp(arg, "away",      len)) st = away;
+  else if (!strncasecmp(arg, "invisible", len)) st = invisible;
+  else if (!strncasecmp(arg, "dnd",       len)) st = dontdisturb;
+  else if (!strncasecmp(arg, "notavail",  len)) st = notavail;
+  else if (!strncasecmp(arg, "free",      len)) st = freeforchat;
   else {
     scr_LogPrint("Unrecognized parameter!");
     return;
   }
 
-  // XXX special case if offline??
-  jb_setstatus(st, NULL);  // TODO handle message (instead of NULL)
+  if (msg && st != offline && st != invisible) {
+    for (msg++ ; *msg && *msg == ' ' ; msg++) ;
+    if (!*msg) msg = NULL;
+  } else
+    msg = NULL;
+
+  jb_setstatus(st, msg);
 }
 
-void do_add(char *arg)
+static void do_add(char *arg)
 {
   char *id, *nick;
   if (!arg || (*arg == 0)) {
@@ -414,7 +430,7 @@
   g_free(id);
 }
 
-void do_del(char *arg)
+static void do_del(char *arg)
 {
   const char *jid;
 
@@ -431,7 +447,7 @@
   jb_delbuddy(jid);
 }
 
-void do_group(char *arg)
+static void do_group(char *arg)
 {
   gpointer group;
   guint leave_windowbuddy;
@@ -471,7 +487,7 @@
   if (leave_windowbuddy) scr_ShowBuddyWindow();
 }
 
-void do_say(char *arg)
+static void do_say(char *arg)
 {
   gpointer bud;
 
@@ -492,7 +508,7 @@
   send_message(arg);
 }
 
-void do_msay(char *arg)
+static void do_msay(char *arg)
 {
   /* Parameters: begin verbatim abort send */
   gpointer bud;
@@ -546,24 +562,24 @@
   scr_set_multimode(FALSE);
 }
 
-void do_buffer(char *arg)
+static void do_buffer(char *arg)
 {
   if (!strcasecmp(arg, "top")) {
-    scr_BufferTop();
+    scr_BufferTopBottom(-1);
   } else if (!strcasecmp(arg, "bottom")) {
-    scr_BufferBottom();
+    scr_BufferTopBottom(1);
   } else if (!strcasecmp(arg, "clear")) {
     scr_Clear();
   } else
     scr_LogPrint("Unrecognized parameter!");
 }
 
-void do_clear(char *arg)    // Alias for "/buffer clear"
+static void do_clear(char *arg)    // Alias for "/buffer clear"
 {
   do_buffer("clear");
 }
 
-void do_info(char *arg)
+static void do_info(char *arg)
 {
   gpointer bud;
   const char *jid, *name, *st_msg;
@@ -610,7 +626,7 @@
   g_free(buffer);
 }
 
-void do_rename(char *arg)
+static void do_rename(char *arg)
 {
   gpointer bud;
   const char *jid, *group;
@@ -646,7 +662,7 @@
   update_roster = TRUE;
 }
 
-void do_move(char *arg)
+static void do_move(char *arg)
 {
   gpointer bud;
   const char *jid, *name;
@@ -680,7 +696,7 @@
   update_roster = TRUE;
 }
 
-void do_set(char *arg)
+static void do_set(char *arg)
 {
   guint assign;
   const gchar *option, *value;
@@ -710,7 +726,7 @@
   }
 }
 
-void do_alias(char *arg)
+static void do_alias(char *arg)
 {
   guint assign;
   const gchar *alias, *value;
@@ -749,7 +765,7 @@
   }
 }
 
-void do_bind(char *arg)
+static void do_bind(char *arg)
 {
   guint assign;
   const gchar *keycode, *value;
@@ -775,12 +791,12 @@
     settings_set(SETTINGS_TYPE_BINDING, keycode, value);
 }
 
-void do_connect(char *arg)
+static void do_connect(char *arg)
 {
   mcabber_connect();
 }
 
-void do_disconnect(char *arg)
+static void do_disconnect(char *arg)
 {
   jb_disconnect();
 }
--- a/mcabber/src/hbuf.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/hbuf.h	Sun Jul 24 15:13:53 2005 +0100
@@ -19,6 +19,7 @@
 #define HBB_PREFIX_STATUS   4
 #define HBB_PREFIX_AUTH     8
 #define HBB_PREFIX_INFO    16
+#define HBB_PREFIX_ERR     32
 
 typedef struct {
   time_t timestamp;
--- a/mcabber/src/histolog.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/histolog.c	Sun Jul 24 15:13:53 2005 +0100
@@ -94,7 +94,7 @@
    *
    * Types:
    * - M message    Info: S (send) R (receive)
-   * - S status     Info: [oaifdcn]
+   * - S status     Info: [_oifdna]
    * We don't check them, we'll trust the caller.
    */
 
@@ -229,7 +229,7 @@
       int l = strlen(root_dir);
       if (l < 1) {
         scr_LogPrint("root_dir too short");
-        UseFileLogging = FALSE;
+        UseFileLogging = FileLoadLogs = FALSE;
         return;
       }
       // RootDir must be slash-terminated
@@ -247,11 +247,16 @@
       strcpy(RootDir, home);
       strcat(RootDir, dir);
     }
-    // FIXME
-    // We should check the directory actually exists
-  } else    // Disable history logging
-    if (RootDir) {
-    g_free(RootDir);
+    // Check directory permissions (should not be readable by group/others)
+    if (checkset_perm(RootDir, TRUE) == -1) {
+      // The directory does not actually exists
+      g_free(RootDir);
+      scr_LogPrint("ERROR: Can't access history log directory");
+      UseFileLogging = FileLoadLogs = FALSE;
+    }
+  } else {  // Disable history logging
+    if (RootDir)
+      g_free(RootDir);
   }
 }
 
--- a/mcabber/src/hooks.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/hooks.c	Sun Jul 24 15:13:53 2005 +0100
@@ -27,12 +27,15 @@
 #include "roster.h"
 #include "histolog.h"
 #include "utf8.h"
+#include "hbuf.h"
 
-static char *extcommand;
+static char *extcmd;
 
-inline void hk_message_in(const char *jid, time_t timestamp, const char *msg)
+inline void hk_message_in(const char *jid, time_t timestamp, const char *msg,
+                          const char *type)
 {
   int new_guy = FALSE;
+  int message_flags;
 
   // If this user isn't in the roster, we add it
   if (!roster_exists(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
@@ -40,11 +43,20 @@
     new_guy = TRUE;
   }
 
+  if (type && !strcmp(type, "error")) {
+    message_flags = HBB_PREFIX_ERR | HBB_PREFIX_IN;
+    scr_LogPrint("Error message received from <%s>", jid);
+  } else
+    message_flags = 0;
+
   // Note: the hlog_write should not be called first, because in some
   // cases scr_WriteIncomingMessage() will load the history and we'd
   // have the message twice...
-  scr_WriteIncomingMessage(jid, msg, timestamp, 0);
-  hlog_write_message(jid, timestamp, FALSE, msg);
+  scr_WriteIncomingMessage(jid, msg, timestamp, message_flags);
+  // We don't log the message if it is an error message
+  if (!(message_flags & HBB_PREFIX_ERR))
+    hlog_write_message(jid, timestamp, FALSE, msg);
+  // External command
   hk_ext_cmd(jid, 'M', 'R', NULL);
   // We need to rebuild the list if the sender is unknown or
   // if the sender is offline/invisible and hide_offline_buddies is set
@@ -60,27 +72,33 @@
 {
   scr_WriteOutgoingMessage(jid, msg);
   hlog_write_message(jid, timestamp, TRUE, msg);
+  // External command
+  hk_ext_cmd(jid, 'M', 'S', NULL);
 }
 
 inline void hk_statuschange(const char *jid, time_t timestamp, 
         enum imstatus status, const char *status_msg)
 {
-  scr_LogPrint("Buddy status has changed: [%c>%c] <%s>",
-          imstatus2char[roster_getstatus(jid)], imstatus2char[status], jid);
+  scr_LogPrint("Buddy status has changed: [%c>%c] <%s> %s",
+          imstatus2char[roster_getstatus(jid)], imstatus2char[status], jid,
+          ((status_msg) ? status_msg : ""));
   roster_setstatus(jid, status, status_msg);
   buddylist_build();
   scr_DrawRoster();
   hlog_write_status(jid, 0, status, status_msg);
+  // External command
+  hk_ext_cmd(jid, 'S', imstatus2char[status], NULL);
 }
 
 inline void hk_mystatuschange(time_t timestamp,
-        enum imstatus old_status, enum imstatus new_status)
+        enum imstatus old_status, enum imstatus new_status, const char *msg)
 {
-  if (old_status == new_status)
+  if (!msg && (old_status == new_status))
     return;
 
-  scr_LogPrint("Your status has changed:  [%c>%c]",
-          imstatus2char[old_status], imstatus2char[new_status]);
+  scr_LogPrint("Your status has changed:  [%c>%c] %s",
+          imstatus2char[old_status], imstatus2char[new_status],
+          ((msg) ? msg : ""));
   //hlog_write_status(NULL, 0, status);
 }
 
@@ -92,12 +110,12 @@
 // Can be called with parameter NULL to reset and free memory.
 void hk_ext_cmd_init(const char *command)
 {
-  if (extcommand) {
-    g_free(extcommand);
-    extcommand = NULL;
+  if (extcmd) {
+    g_free(extcmd);
+    extcmd = NULL;
   }
   if (command)
-    extcommand = g_strdup(command);
+    extcmd = g_strdup(command);
 }
 
 //  hk_ext_cmd()
@@ -106,21 +124,44 @@
 void hk_ext_cmd(const char *jid, guchar type, guchar info, const char *data)
 {
   pid_t pid;
+  char *arg_type = NULL;
+  char *arg_info = NULL;
+  char *arg_data = NULL;
+  char status_str[2];
 
-  if (!extcommand) return;
+  if (!extcmd) return;
 
-  // For now we'll only handle incoming messages
-  if (type != 'M') return;
-  if (info != 'R') return;
+  // Prepare arg_* (external command parameters)
+  switch (type) {
+    case 'M':
+        arg_type = "MSG";
+        if (info == 'R')
+          arg_info = "IN";
+        else if (info == 'S')
+          arg_info = "OUT";
+
+        break;
+    case 'S':
+        arg_type = "STATUS";
+        if (strchr(imstatus2char, tolower(info))) {
+          status_str[0] = toupper(info);
+          status_str[1] = 0;
+          arg_info = status_str;
+        }
+        break;
+    default:
+        return;
+  }
+
+  if (!arg_type || !arg_info) return;
 
   if ((pid=fork()) == -1) {
     scr_LogPrint("Fork error, cannot launch external command.");
     return;
   }
 
-  // I don't remember what I should do with the parent process...
   if (pid == 0) { // child
-    if (execl(extcommand, extcommand, "MSG", "IN", jid, NULL) == -1) {
+    if (execl(extcmd, extcmd, arg_type, arg_info, jid, arg_data) == -1) {
       // ut_WriteLog("Cannot execute external command.\n");
       exit(1);
     }
--- a/mcabber/src/hooks.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/hooks.h	Sun Jul 24 15:13:53 2005 +0100
@@ -5,12 +5,13 @@
 #include "jabglue.h"
 
 
-inline void hk_message_in(const char *jid, time_t timestamp, const char *msg);
+inline void hk_message_in(const char *jid, time_t timestamp, const char *msg,
+                          const char *type);
 inline void hk_message_out(const char *jid, time_t timestamp, const char *msg);
 inline void hk_statuschange(const char *jid, time_t timestamp, 
         enum imstatus status, char const *status_msg);
 inline void hk_mystatuschange(time_t timestamp,
-        enum imstatus old_status, enum imstatus new_status);
+        enum imstatus old_status, enum imstatus new_status, const char *msg);
 
 void hk_ext_cmd_init(const char *command);
 void hk_ext_cmd(const char *jid, guchar type, guchar info, const char *data);
--- a/mcabber/src/jabglue.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/jabglue.c	Sun Jul 24 15:13:53 2005 +0100
@@ -45,8 +45,8 @@
 static enum imstatus mystatus = offline;
 unsigned char online;
 
-char imstatus2char[imstatus_size] = {
-    '_', 'o', 'i', 'f', 'd', 'n', 'a'
+char imstatus2char[imstatus_size+1] = {
+    '_', 'o', 'i', 'f', 'd', 'n', 'a', '\0'
 };
 
 static enum {
@@ -139,8 +139,7 @@
       port = JABBERPORT;
   }
 
-  //if (jc)
-  //  free(jc); XXX
+  jb_disconnect();
 
   s_id = 1;
   jc = jab_new((char*)jid, (char*)pass, port, ssl);
@@ -250,6 +249,7 @@
 void jb_setstatus(enum imstatus st, const char *msg)
 {
   xmlnode x;
+  char *utf8_msg;
 
   if (!online) return;
 
@@ -298,11 +298,13 @@
   if (!msg)
       msg = settings_get_status_msg(st);
 
-  xmlnode_insert_cdata(xmlnode_insert_tag(x, "status"), msg,
+  utf8_msg = utf8_encode(msg);
+  xmlnode_insert_cdata(xmlnode_insert_tag(x, "status"), utf8_msg,
           (unsigned) -1);
 
   jab_send(jc, x);
   xmlnode_free(x);
+  free(utf8_msg);
 
   //sendvisibility();   ???
 
@@ -311,7 +313,7 @@
   if (mystatus == offline || st == offline)
     update_roster = TRUE;
 
-  hk_mystatuschange(0, mystatus, st);
+  hk_mystatuschange(0, mystatus, st, msg);
   mystatus = st;
 }
 
@@ -563,7 +565,7 @@
   */
 
   jid = jidtodisp(from);
-  hk_message_in(jid, timestamp, buffer);
+  hk_message_in(jid, timestamp, buffer, type);
   g_free(jid);
   free(buffer);
 }
@@ -577,7 +579,7 @@
   switch(state) {
     case JCONN_STATE_OFF:
         if (previous_state != JCONN_STATE_OFF)
-          scr_LogPrint("+ JCONN_STATE_OFF");
+          scr_LogPrint("[Jabber] Not connected to the server");
 
         online = FALSE;
         mystatus = offline;
@@ -586,20 +588,21 @@
         break;
 
     case JCONN_STATE_CONNECTED:
-        scr_LogPrint("+ JCONN_STATE_CONNECTED");
+        scr_LogPrint("[Jabber] Connected to the server");
         break;
 
     case JCONN_STATE_AUTH:
-        scr_LogPrint("+ JCONN_STATE_AUTH");
+        scr_LogPrint("[Jabber] Authenticating to the server");
         break;
 
     case JCONN_STATE_ON:
-        scr_LogPrint("+ JCONN_STATE_ON");
+        scr_LogPrint("[Jabber] Communication with the server established");
         online = TRUE;
         break;
 
     case JCONN_STATE_CONNECTING:
-        scr_LogPrint("+ JCONN_STATE_CONNECTING");
+        if (previous_state != state)
+        scr_LogPrint("[Jabber] Connecting to the server");
         break;
 
     default:
@@ -611,6 +614,7 @@
 void packethandler(jconn conn, jpacket packet)
 {
   char *p, *r;
+  const char *m;
   xmlnode x, y;
   char *from=NULL, *type=NULL, *body=NULL, *enc=NULL;
   char *ns=NULL;
@@ -851,19 +855,22 @@
           }
         }
 
-        if (type && !strcmp(type, "unavailable")) {
+        if (type && !strcmp(type, "unavailable"))
           ust = offline;
-        }
 
         if ((x = xmlnode_get_tag(packet->x, "status")) != NULL)
-          p = xmlnode_get_data(x);
+          p = utf8_decode(xmlnode_get_data(x));
         else
           p = NULL;
 
         r = jidtodisp(from);
-        if (ust != roster_getstatus(r))
+        // Call hk_statuschange() if status has changed or if the
+        // status message is different
+        m = roster_getstatusmsg(r);
+        if ((ust != roster_getstatus(r)) || (p && (!m || strcmp(p, m))))
           hk_statuschange(r, 0, ust, p);
         g_free(r);
+        if (p) free(p);
         break;
 
     case JPACKET_S10N:
--- a/mcabber/src/main.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/main.c	Sun Jul 24 15:13:53 2005 +0100
@@ -34,7 +34,6 @@
 
 #include "jabglue.h"
 #include "screen.h"
-#include "parsecfg.h"
 #include "settings.h"
 #include "roster.h"
 #include "commands.h"
@@ -77,8 +76,12 @@
   jb_set_priority(settings_opt_get_int("priority"));
 
   /* Connect to server */
-  ut_WriteLog("Connecting to server: %s:%d\n", servername, port);
-  scr_LogPrint("Connecting to server: %s:%d", servername, port);
+  ut_WriteLog("Connecting to server: %s\n", servername);
+  scr_LogPrint("Connecting to server: %s", servername);
+  if (port) {
+    ut_WriteLog(" using port %d\n", port);
+    scr_LogPrint(" using port %d", port);
+  }
 
   jid = compose_jid(username, servername, resource);
   jc = jb_connect(jid, port, ssl, password);
@@ -131,7 +134,7 @@
   }
 }
 
-void ask_password(void)
+static void ask_password(void)
 {
   char *password, *p;
   size_t passsize = 128;
@@ -165,7 +168,7 @@
   return;
 }
 
-void credits(void)
+static void credits(void)
 {
   printf(MCABBER_VERSION "\n");
   printf(EMAIL "\n");
@@ -178,7 +181,7 @@
   int optval, optval2;
   int key;
   unsigned int ping;
-  int ret = 0;
+  int ret;
   unsigned int refresh = 0;
 
   credits();
@@ -209,13 +212,19 @@
       }
   }
 
+  /* Initialize commands system */
+  cmd_init();
+
   if (configFile)
     ut_WriteLog("Setting config file: %s\n", configFile);
 
   /* Parsing config file... */
   ut_WriteLog("Parsing config file...\n");
-  cfg_file(configFile);
+  ret = cfg_read_file(configFile);
   if (configFile) g_free(configFile);
+  /* Leave if there was an error in the config. file */
+  if (ret)
+    exit(EXIT_FAILURE);
 
   optstring = settings_opt_get("debug");
   if (optstring) ut_InitDebug(1, optstring);
@@ -256,18 +265,16 @@
   else
     scr_LogPrint("Can't connect: no password supplied");
 
-  /* Initialize commands system */
-  cmd_init();
-
   ut_WriteLog("Entering into main loop...\n\n");
   ut_WriteLog("Ready to send/receive messages...\n");
 
-  while (ret != 255) {
+  for (ret = 0 ; ret != 255 ; ) {
     key = scr_Getch();
 
     /* The refresh is really an ugly hack, but we need to call doupdate()
        from time to time to catch the RESIZE events, because getch keep
        returning ERR until a real key is pressed :-(
+       However, it allows us to handle an autoaway check here...
      */
     if (key != ERR) {
       ret = process_key(key);
@@ -275,6 +282,7 @@
     } else if (refresh++ > 1) {
       doupdate();
       refresh = 0;
+      scr_CheckAutoAway(FALSE);
     }
 
     if (key != KEY_RESIZE)
--- a/mcabber/src/parsecfg.c	Fri Jul 15 10:56:15 2005 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <glib.h>
-
-#include "settings.h"
-#include "utils.h"
-
-//  cfg_file(filename)
-// Read and parse config file "filename".  If filename is NULL,
-// try to open the configuration file at the default locations.
-//
-// This function comes from Cabber, and has been slightly modified.
-int cfg_file(char *filename)
-{
-  FILE *fp;
-  char *buf;
-  char *line;
-  char *value;
-
-  if (!filename) {
-    // Use default config file locations
-    char *home = getenv("HOME");
-    if (!home) {
-      ut_WriteLog("Can't find home dir!\n");
-      exit(EXIT_FAILURE);
-    }
-    filename = g_new(char, strlen(home)+24);
-    sprintf(filename, "%s/.mcabber/mcabberrc", home);
-    if ((fp = fopen(filename, "r")) == NULL) {
-      // 2nd try...
-      sprintf(filename, "%s/.mcabberrc", home);
-      if ((fp = fopen(filename, "r")) == NULL) {
-        fprintf(stderr, "Cannot open config file!\n");
-        exit(EXIT_FAILURE);
-      }
-    }
-    g_free(filename);
-  }
-  else if ((fp = fopen(filename, "r")) == NULL) {
-    perror("fopen (parsecfg.c:46)");
-    exit(EXIT_FAILURE);
-  }
-
-  buf = g_new(char, 256);
-
-  while (fgets(buf, 256, fp) != NULL) {
-    line = buf;
-
-    while (isspace((int) *line))
-      line++;
-
-    while ((strlen(line) > 0)
-	   && isspace((int) line[strlen(line) - 1]))
-      line[strlen(line) - 1] = '\0';
-
-    if ((*line == '\n') || (*line == '\0') || (*line == '#'))
-      continue;
-
-    if ((strchr(line, '=') != NULL)) {
-      value = strchr(line, '=');
-      *value = '\0';
-      value++;
-
-      while (isspace((int) *value))
-	value++;
-
-      while ((strlen(line) > 0)
-	     && isspace((int) line[strlen(line) - 1]))
-	line[strlen(line) - 1] = '\0';
-
-      settings_set(SETTINGS_TYPE_OPTION, line, value);
-      continue;
-    }
-    fprintf(stderr, "CFG: orphaned line \"%s\"\n", line);
-  }
-  g_free(buf);
-  return 1;
-}
--- a/mcabber/src/parsecfg.h	Fri Jul 15 10:56:15 2005 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef __PARSECFG_H__
-#define __PARSECFG_H__ 1
-
-int cfg_file(char *filename);
-char *cfg_read(char *key);
-int cfg_read_int(char *key);
-
-#endif
--- a/mcabber/src/roster.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/roster.c	Sun Jul 24 15:13:53 2005 +0100
@@ -46,19 +46,20 @@
 static GSList *unread_list;
 GList *buddylist;
 GList *current_buddy;
+GList *alternate_buddy;
 
 
 /* ### Roster functions ### */
 
 // Comparison function used to search in the roster (compares jids and types)
-gint roster_compare_jid_type(roster *a, roster *b) {
+static gint roster_compare_jid_type(roster *a, roster *b) {
   if (! (a->type & b->type))
     return -1; // arbitrary (but should be != , of course)
   return strcasecmp(a->jid, b->jid);
 }
 
 // Comparison function used to sort the roster (by name)
-gint roster_compare_name(roster *a, roster *b) {
+static gint roster_compare_name(roster *a, roster *b) {
   return strcasecmp(a->name, b->name);
 }
 
@@ -359,6 +360,19 @@
   return roster_usr->status;
 }
 
+const char *roster_getstatusmsg(const char *jid)
+{
+  GSList *sl_user;
+  roster *roster_usr;
+
+  sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
+  if (sl_user == NULL)
+    return offline; // Not in the roster, anyway...
+
+  roster_usr = (roster*)sl_user->data;
+  return roster_usr->status_msg;
+}
+
 guint roster_gettype(const char *jid)
 {
   GSList *sl_user;
@@ -406,12 +420,16 @@
   GSList *sl_roster_elt = groups;
   roster *roster_elt;
   roster *roster_current_buddy = NULL;
+  roster *roster_alternate_buddy = NULL;
   int shrunk_group;
 
   // We need to remember which buddy is selected.
   if (current_buddy)
     roster_current_buddy = BUDDATA(current_buddy);
   current_buddy = NULL;
+  if (alternate_buddy)
+    roster_alternate_buddy = BUDDATA(alternate_buddy);
+  alternate_buddy = NULL;
 
   // Destroy old buddylist
   if (buddylist) {
@@ -474,6 +492,8 @@
   // Check if we can find our saved current_buddy...
   if (roster_current_buddy)
     current_buddy = g_list_find(buddylist, roster_current_buddy);
+  if (roster_alternate_buddy)
+    alternate_buddy = g_list_find(buddylist, roster_alternate_buddy);
   // current_buddy initialization
   if (!current_buddy || (g_list_position(buddylist, current_buddy) == -1))
     current_buddy = g_list_first(buddylist);
@@ -511,6 +531,7 @@
   GSList **sl_group;
   GSList *sl_clone;
   roster *roster_clone;
+  int is_alternate;
 
   // A group has no group :)
   if (roster_usr->type & ROSTER_TYPE_GROUP) return;
@@ -537,8 +558,11 @@
   ((roster*)((GSList*)roster_clone->list)->data)->flags &= ~ROSTER_FLAG_HIDE;
 
   // Little trick to have current_body pointing to the cloned buddy
+  is_alternate = (alternate_buddy == current_buddy);
   buddylist = g_list_append(buddylist, roster_clone);
   current_buddy = g_list_find(buddylist, roster_clone);
+  if (is_alternate)
+    alternate_buddy = current_buddy;
 
   buddylist_build();
 }
--- a/mcabber/src/roster.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/roster.h	Sun Jul 24 15:13:53 2005 +0100
@@ -23,6 +23,7 @@
 
 extern GList *buddylist;
 extern GList *current_buddy;
+extern GList *alternate_buddy;
 
 // Macros...
 
@@ -41,6 +42,7 @@
 void    roster_msg_setflag(const char *jid, guint value);
 void    roster_settype(const char *jid, guint type);
 enum imstatus roster_getstatus(const char *jid);
+const char   *roster_getstatusmsg(const char *jid);
 guint   roster_gettype(const char *jid);
 inline guint roster_exists(const char *jidname, enum findwhat type,
         guint roster_type);
--- a/mcabber/src/screen.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/screen.c	Sun Jul 24 15:13:53 2005 +0100
@@ -42,7 +42,7 @@
 
 #define window_entry(n) list_entry(n, window_entry_t, list)
 
-inline void check_offset(int);
+static inline void check_offset(int);
 
 LIST_HEAD(window_list);
 
@@ -69,6 +69,8 @@
 static char *multiline;
 int update_roster;
 int utf8_mode = 0;
+static bool Autoaway;
+static bool Curses;
 
 static char       inputLine[INPUTLINE_LENGTH+1];
 static char      *ptr_inputline;
@@ -81,27 +83,15 @@
 
 /* Functions */
 
-int scr_WindowWidth(WINDOW * win)
+static int scr_WindowWidth(WINDOW * win)
 {
   int x, y;
   getmaxyx(win, y, x);
   return x;
 }
 
-void scr_clear_box(WINDOW *win, int y, int x, int height, int width, int Color)
-{
-  int i, j;
-
-  wattrset(win, COLOR_PAIR(Color));
-  for (i = 0; i < height; i++) {
-    wmove(win, y + i, x);
-    for (j = 0; j < width; j++)
-      wprintw(win, " ");
-  }
-}
-
-void scr_draw_box(WINDOW * win, int y, int x, int height, int width,
-                  int Color, chtype box, chtype border)
+static void scr_draw_box(WINDOW * win, int y, int x, int height, int width,
+                         int Color, chtype box, chtype border)
 {
   int i, j;
 
@@ -130,7 +120,7 @@
   }
 }
 
-int FindColor(const char *name)
+static int FindColor(const char *name)
 {
   if (!strcmp(name, "default"))
     return -1;
@@ -154,7 +144,7 @@
   return -1;
 }
 
-void ParseColors(void)
+static void ParseColors(void)
 {
   const char *colors[8] = {
     "", "",
@@ -207,8 +197,7 @@
   }
 }
 
-
-window_entry_t *scr_CreateBuddyPanel(const char *title, int dont_show)
+static window_entry_t *scr_CreateBuddyPanel(const char *title, int dont_show)
 {
   int x;
   int y;
@@ -254,11 +243,13 @@
   return tmp;
 }
 
-window_entry_t *scr_SearchWindow(const char *winId)
+static window_entry_t *scr_SearchWindow(const char *winId)
 {
   struct list_head *pos, *n;
   window_entry_t *search_entry = NULL;
 
+  if (!winId) return NULL;
+
   list_for_each_safe(pos, n, &window_list) {
     search_entry = window_entry(pos);
     if (search_entry->name) {
@@ -272,7 +263,7 @@
 
 //  scr_UpdateWindow()
 // (Re-)Display the given chat window.
-void scr_UpdateWindow(window_entry_t *win_entry)
+static void scr_UpdateWindow(window_entry_t *win_entry)
 {
   int n;
   int width;
@@ -328,6 +319,13 @@
         else if (line->flags & HBB_PREFIX_OUT)
           dir = '>';
         wprintw(win_entry->win, "%.11s *%c* ", date, dir);
+      } else if (line->flags & HBB_PREFIX_ERR) {
+        char dir = '#';
+        if (line->flags & HBB_PREFIX_IN)
+          dir = '<';
+        else if (line->flags & HBB_PREFIX_OUT)
+          dir = '>';
+        wprintw(win_entry->win, "%.11s #%c# ", date, dir);
       } else if (line->flags & HBB_PREFIX_IN)
         wprintw(win_entry->win, "%.11s <== ", date);
       else if (line->flags & HBB_PREFIX_OUT)
@@ -348,7 +346,7 @@
 
 //  scr_ShowWindow()
 // Display the chat window with the given identifier.
-void scr_ShowWindow(const char *winId)
+static void scr_ShowWindow(const char *winId)
 {
   window_entry_t *win_entry = scr_SearchWindow(winId);
 
@@ -461,6 +459,7 @@
   halfdelay(5);
   start_color();
   use_default_colors();
+  Curses = TRUE;
 
   ParseColors();
 
@@ -478,12 +477,55 @@
 
 void scr_TerminateCurses(void)
 {
+  if (!Curses) return;
   clear();
   refresh();
   endwin();
+  Curses = FALSE;
   return;
 }
 
+void inline set_autoaway(bool setaway)
+{
+  static enum imstatus oldstatus;
+  Autoaway = setaway;
+
+  if (setaway) {
+    const char *msg;
+    oldstatus = jb_getstatus();
+    msg = settings_opt_get("message_autoaway");
+    if (!msg) msg = MSG_AUTOAWAY;
+    jb_setstatus(away, msg);
+  } else {
+    // Back
+    jb_setstatus(oldstatus, NULL);
+  }
+}
+
+// Check if we should enter/leave automatic away status
+void scr_CheckAutoAway(bool activity)
+{
+  static time_t LastActivity;
+  enum imstatus cur_st;
+  unsigned int autoaway_timeout = settings_opt_get_int("autoaway");
+
+  if (Autoaway && activity) set_autoaway(FALSE);
+  if (!autoaway_timeout) return;
+  if (!LastActivity || activity) time(&LastActivity);
+
+  cur_st = jb_getstatus();
+  // Auto-away is disabled for the following states
+  if ((cur_st != available) && (cur_st != freeforchat))
+    return;
+
+  if (!activity) {
+    time_t now;
+    time(&now);
+    if (!Autoaway && (now > LastActivity + autoaway_timeout))
+      set_autoaway(TRUE);
+  }
+}
+
 //  scr_DrawMainWindow()
 // Set fullinit to TRUE to also create panels.  Set it to FALSE for a resize.
 //
@@ -499,6 +541,11 @@
     logWnd_border = newwin(LOG_WIN_HEIGHT, maxX, CHAT_WIN_HEIGHT, 0);
     logWnd    = newwin(LOG_WIN_HEIGHT-2, maxX-2, CHAT_WIN_HEIGHT+1, 1);
     inputWnd  = newwin(1, maxX, maxY-1, 0);
+    if (!rosterWnd || !chatWnd || !logWnd || !inputWnd) {
+      scr_TerminateCurses();
+      fprintf(stderr, "Cannot create windows!\n");
+      exit(EXIT_FAILURE);
+    }
     wbkgd(rosterWnd,      COLOR_PAIR(COLOR_GENERAL));
     wbkgd(chatWnd,        COLOR_PAIR(COLOR_GENERAL));
     wbkgd(logWnd_border,  COLOR_PAIR(COLOR_GENERAL));
@@ -608,7 +655,7 @@
 }
 
 //  scr_DrawRoster()
-// Actually, display the buddylist on the screen.
+// Display the buddylist (not really the roster) on the screen
 void scr_DrawRoster(void)
 {
   static guint offset = 0;
@@ -743,98 +790,59 @@
   return ch;
 }
 
-WINDOW *scr_GetRosterWindow(void)
-{
-  return rosterWnd;
-}
-
-WINDOW *scr_GetStatusWindow(void)
+//  set_current_buddy(newbuddy)
+// Set the current_buddy to newbuddy (if not NULL)
+// Lock the newbuddy, and unlock the previous current_buddy
+static void set_current_buddy(GList *newbuddy)
 {
-  return chatWnd;
-}
+  enum imstatus prev_st = imstatus_size;
+  /* prev_st initialized to imstatus_size, which is used as "undef" value.
+   * We are sure prev_st will get a different status value after the
+   * buddy_getstatus() call.
+   */
+
+  if (!current_buddy || !newbuddy)  return;
+  if (newbuddy == current_buddy)    return;
 
-WINDOW *scr_GetInputWindow(void)
-{
-  return inputWnd;
+  prev_st = buddy_getstatus(BUDDATA(current_buddy));
+  buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
+  if (chatmode)
+    alternate_buddy = current_buddy;
+  current_buddy = newbuddy;
+  // Lock the buddy in the buddylist if we're in chat mode
+  if (chatmode)
+    buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
+  // We should rebuild the buddylist but not everytime
+  // Here we check if we were locking a buddy who is actually offline,
+  // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
+  if (prev_st == offline && buddylist_get_hide_offline_buddies())
+    buddylist_build();
+  update_roster = TRUE;
 }
 
 //  scr_RosterTop()
 // Go to the first buddy in the buddylist
 void scr_RosterTop(void)
 {
-  enum imstatus prev_st = imstatus_size; // undef
-
-  if (current_buddy) {
-    prev_st = buddy_getstatus(BUDDATA(current_buddy));
-    if (chatmode)
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-  }
-  current_buddy = buddylist;
-  if (chatmode && current_buddy)
-    buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-
-  // We should rebuild the buddylist but not everytime
-  // Here we check if we were locking a buddy who is actually offline,
-  // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-  if (current_buddy && prev_st == offline &&
-          buddylist_get_hide_offline_buddies())
-    buddylist_build();
+  set_current_buddy(buddylist);
   if (chatmode)
     scr_ShowBuddyWindow();
-  update_roster = TRUE;
 }
 
 //  scr_RosterBottom()
 // Go to the last buddy in the buddylist
 void scr_RosterBottom(void)
 {
-  enum imstatus prev_st = imstatus_size; // undef
-
-  if (current_buddy) {
-    prev_st = buddy_getstatus(BUDDATA(current_buddy));
-    if (chatmode)
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-  }
-  current_buddy = g_list_last(buddylist);
-  // Lock the buddy in the buddylist if we're in chat mode
-  if (chatmode && current_buddy)
-    buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-
-  // We should rebuild the buddylist but not everytime
-  // Here we check if we were locking a buddy who is actually offline,
-  // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-  if (current_buddy && prev_st == offline &&
-          buddylist_get_hide_offline_buddies())
-    buddylist_build();
-
+  set_current_buddy(g_list_last(buddylist));
   if (chatmode)
     scr_ShowBuddyWindow();
-  update_roster = TRUE;
 }
 
 //  scr_RosterUp()
 // Go to the previous buddy in the buddylist
 void scr_RosterUp(void)
 {
-  enum imstatus prev_st = imstatus_size; // undef
-
-  if (current_buddy) {
-    if (g_list_previous(current_buddy)) {
-      prev_st = buddy_getstatus(BUDDATA(current_buddy));
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-      current_buddy = g_list_previous(current_buddy);
-      // Lock the buddy in the buddylist if we're in chat mode
-      if (chatmode)
-        buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-      // We should rebuild the buddylist but not everytime
-      // Here we check if we were locking a buddy who is actually offline,
-      // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-      if (prev_st == offline && buddylist_get_hide_offline_buddies())
-        buddylist_build();
-      update_roster = TRUE;
-    }
-  }
-
+  set_current_buddy(g_list_previous(current_buddy));
   if (chatmode)
     scr_ShowBuddyWindow();
 }
@@ -843,24 +851,7 @@
 // Go to the next buddy in the buddylist
 void scr_RosterDown(void)
 {
-  enum imstatus prev_st = imstatus_size; // undef
-
-  if (current_buddy) {
-    if (g_list_next(current_buddy)) {
-      prev_st = buddy_getstatus(BUDDATA(current_buddy));
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-      current_buddy = g_list_next(current_buddy);
-      if (chatmode)
-        buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-      // We should rebuild the buddylist but not everytime
-      // Here we check if we were locking a buddy who is actually offline,
-      // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-      if (prev_st == offline && buddylist_get_hide_offline_buddies())
-        buddylist_build();
-      update_roster = TRUE;
-    }
-  }
-
+  set_current_buddy(g_list_next(current_buddy));
   if (chatmode)
     scr_ShowBuddyWindow();
 }
@@ -869,26 +860,7 @@
 // Look forward for a buddy with jid/name containing str.
 void scr_RosterSearch(char *str)
 {
-  GList *matching_buddy;
-  enum imstatus prev_st = imstatus_size; // undef
-
-  if (current_buddy) {
-    matching_buddy = buddy_search(str);
-    if (matching_buddy) {
-      prev_st = buddy_getstatus(BUDDATA(current_buddy));
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-      current_buddy = matching_buddy;
-      if (chatmode)
-        buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-      // We should rebuild the buddylist but not everytime
-      // Here we check if we were locking a buddy who is actually offline,
-      // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-      if (prev_st == offline && buddylist_get_hide_offline_buddies())
-        buddylist_build();
-      update_roster = TRUE;
-    }
-  }
-
+  set_current_buddy(buddy_search(str));
   if (chatmode)
     scr_ShowBuddyWindow();
 }
@@ -899,122 +871,83 @@
 // message from unread_list.
 void scr_RosterUnreadMessage(int next)
 {
-  enum imstatus prev_st = imstatus_size; // undef
+  gpointer unread_ptr;
+  gpointer refbuddata;
+  gpointer ngroup;
+  GList *nbuddy;
 
-  if (current_buddy) {
-    gpointer unread_ptr;
-    gpointer refbuddata;
-    gpointer ngroup;
-    GList *nbuddy;
+  if (!current_buddy) return;
 
-    if (next) refbuddata = BUDDATA(current_buddy);
-    else      refbuddata = NULL;
+  if (next) refbuddata = BUDDATA(current_buddy);
+  else      refbuddata = NULL;
 
-    unread_ptr = unread_msg(refbuddata);
-    if (!unread_ptr) return;
+  unread_ptr = unread_msg(refbuddata);
+  if (!unread_ptr) return;
 
-    // If buddy is in a folded group, we need to expand it
-    ngroup = buddy_getgroup(unread_ptr);
-    if (buddy_getflags(ngroup) & ROSTER_FLAG_HIDE) {
-      buddy_setflags(ngroup, ROSTER_FLAG_HIDE, FALSE);
-      buddylist_build();
-    }
+  // If buddy is in a folded group, we need to expand it
+  ngroup = buddy_getgroup(unread_ptr);
+  if (buddy_getflags(ngroup) & ROSTER_FLAG_HIDE) {
+    buddy_setflags(ngroup, ROSTER_FLAG_HIDE, FALSE);
+    buddylist_build();
+  }
 
-    nbuddy = g_list_find(buddylist, unread_ptr);
-    if (nbuddy) {
-      prev_st = buddy_getstatus(BUDDATA(current_buddy));
-      buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
-      current_buddy = nbuddy;
-      if (chatmode)
-        buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, TRUE);
-      // We should rebuild the buddylist but not everytime
-      // Here we check if we were locking a buddy who is actually offline,
-      // and hide_offline_buddies is TRUE.  In which case we need to rebuild.
-      if (prev_st == offline && buddylist_get_hide_offline_buddies())
-        buddylist_build();
-      update_roster = TRUE;
-
-      if (chatmode) scr_ShowBuddyWindow();
-    } else scr_LogPrint("Error: nbuddy == NULL");
-  }
+  nbuddy = g_list_find(buddylist, unread_ptr);
+  if (nbuddy) {
+    set_current_buddy(nbuddy);
+    if (chatmode) scr_ShowBuddyWindow();
+  } else scr_LogPrint("Error: nbuddy == NULL");
 }
 
-//  scr_ScrollUp()
-// Scroll up the current buddy window, half a screen.
-void scr_ScrollUp(void)
+//  scr_RosterJumpAlternate()
+// Try to jump to alternate (== previous) buddy
+void scr_RosterJumpAlternate(void)
 {
-  const gchar *jid;
+  if (!alternate_buddy || g_list_position(buddylist, alternate_buddy) == -1)
+    return;
+  set_current_buddy(alternate_buddy);
+  if (chatmode)
+    scr_ShowBuddyWindow();
+}
+
+//  scr_ScrollUpDown()
+// Scroll up/down the current buddy window, half a screen.
+// (up if updown == -1, down if updown == 1)
+void scr_ScrollUpDown(int updown)
+{
   window_entry_t *win_entry;
   int n, nblines;
   GList *hbuf_top;
 
   // Get win_entry
-  if (!current_buddy)
-    return;
-  jid = CURRENT_JID;
-  if (!jid)
-    return;
-  win_entry  = scr_SearchWindow(jid);
-  if (!win_entry)
-    return;
-
-  // Scroll up half a screen (or less)
-  nblines = CHAT_WIN_HEIGHT/2-1;
-  hbuf_top = win_entry->top;
-  if (!hbuf_top) {
-    hbuf_top = g_list_last(win_entry->hbuf);
-    if (!win_entry->cleared)
-      nblines *= 3;
-    else
-      win_entry->cleared = FALSE;
-  }
-
-  n = 0;
-  while (hbuf_top && n < nblines && g_list_previous(hbuf_top)) {
-    hbuf_top = g_list_previous(hbuf_top);
-    n++;
-  }
-  win_entry->top = hbuf_top;
+  if (!current_buddy) return;
+  win_entry  = scr_SearchWindow(CURRENT_JID);
+  if (!win_entry) return;
 
-  // Refresh the window
-  scr_UpdateWindow(win_entry);
-
-  // Finished :)
-  update_panels();
-  doupdate();
-}
-
-//  scr_ScrollDown()
-// Scroll down the current buddy window, half a screen.
-void scr_ScrollDown(void)
-{
-  const gchar *jid;
-  window_entry_t *win_entry;
-  int n, nblines;
-  GList *hbuf_top;
-
-  // Get win_entry
-  if (!current_buddy)
-    return;
-  jid = CURRENT_JID;
-  if (!jid)
-    return;
-  win_entry  = scr_SearchWindow(jid);
-  if (!win_entry)
-    return;
-
-  // Scroll down half a screen (or less)
+  // Scroll half a screen (or less)
   nblines = CHAT_WIN_HEIGHT/2-1;
   hbuf_top = win_entry->top;
 
-  for (n=0 ; hbuf_top && n < nblines ; n++)
-    hbuf_top = g_list_next(hbuf_top);
-  win_entry->top = hbuf_top;
-  // Check if we are at the bottom
-  for (n=0 ; hbuf_top && n < CHAT_WIN_HEIGHT-1 ; n++)
-    hbuf_top = g_list_next(hbuf_top);
-  if (!hbuf_top)
-    win_entry->top = NULL; // End reached
+  if (updown == -1) {   // UP
+    if (!hbuf_top) {
+      hbuf_top = g_list_last(win_entry->hbuf);
+      if (!win_entry->cleared)
+        nblines *= 3;
+      else
+        win_entry->cleared = FALSE;
+    }
+    for (n=0 ; hbuf_top && n < nblines && g_list_previous(hbuf_top) ; n++)
+      hbuf_top = g_list_previous(hbuf_top);
+    win_entry->top = hbuf_top;
+  } else {              // DOWN
+    for (n=0 ; hbuf_top && n < nblines ; n++)
+      hbuf_top = g_list_next(hbuf_top);
+    win_entry->top = hbuf_top;
+    // Check if we are at the bottom
+    for (n=0 ; hbuf_top && n < CHAT_WIN_HEIGHT-1 ; n++)
+      hbuf_top = g_list_next(hbuf_top);
+    if (!hbuf_top)
+      win_entry->top = NULL; // End reached
+  }
 
   // Refresh the window
   scr_UpdateWindow(win_entry);
@@ -1028,18 +961,12 @@
 // Clear the current buddy window (used for the /clear command)
 void scr_Clear(void)
 {
-  const gchar *jid;
   window_entry_t *win_entry;
 
   // Get win_entry
-  if (!current_buddy)
-    return;
-  jid = CURRENT_JID;
-  if (!jid)
-    return;
-  win_entry  = scr_SearchWindow(jid);
-  if (!win_entry)
-    return;
+  if (!current_buddy) return;
+  win_entry  = scr_SearchWindow(CURRENT_JID);
+  if (!win_entry) return;
 
   win_entry->cleared = TRUE;
   win_entry->top = NULL;
@@ -1052,49 +979,23 @@
   doupdate();
 }
 
-//  scr_BufferTop()
-// Jump to the head of the current buddy window
-void scr_BufferTop(void)
+//  scr_BufferTopBottom()
+// Jump to the head/tail of the current buddy window
+// (top if topbottom == -1, bottom topbottom == 1)
+void scr_BufferTopBottom(int topbottom)
 {
-  const gchar *jid;
   window_entry_t *win_entry;
 
   // Get win_entry
   if (!current_buddy) return;
-  jid = CURRENT_JID;
-  if (!jid) return;
-  win_entry  = scr_SearchWindow(jid);
-
+  win_entry  = scr_SearchWindow(CURRENT_JID);
   if (!win_entry) return;
 
   win_entry->cleared = FALSE;
-  win_entry->top = g_list_first(win_entry->hbuf);
-
-  // Refresh the window
-  scr_UpdateWindow(win_entry);
-
-  // Finished :)
-  update_panels();
-  doupdate();
-}
-
-//  scr_BufferBottom()
-// Jump to the end of the current buddy window
-void scr_BufferBottom(void)
-{
-  const gchar *jid;
-  window_entry_t *win_entry;
-
-  // Get win_entry
-  if (!current_buddy) return;
-  jid = CURRENT_JID;
-  if (!jid) return;
-  win_entry  = scr_SearchWindow(jid);
-
-  if (!win_entry) return;
-
-  win_entry->cleared = FALSE;
-  win_entry->top = NULL;
+  if (topbottom == 1)
+    win_entry->top = NULL;
+  else
+    win_entry->top = g_list_first(win_entry->hbuf);
 
   // Refresh the window
   scr_UpdateWindow(win_entry);
@@ -1118,17 +1019,23 @@
 
   timestamp = time(NULL);
   strftime(buffer, 64, "[%H:%M:%S] ", localtime(&timestamp));
-  wprintw(logWnd, "\n%s", buffer);
+  if (Curses)
+    wprintw(logWnd, "\n%s", buffer);
+  else
+    printf("%s", buffer);
 
   va_start(ap, fmt);
   vsnprintf(buffer, 1024, fmt, ap);
   va_end(ap);
 
-  wprintw(logWnd, "%s", buffer);
+  if (Curses) {
+    wprintw(logWnd, "%s", buffer);
+    update_panels();
+    doupdate();
+  } else {
+    printf("%s\n", buffer);
+  }
   free(buffer);
-
-  update_panels();
-  doupdate();
 }
 
 //  scr_set_chatmode()
@@ -1226,7 +1133,7 @@
 //  scr_cmdhisto_prev()
 // Look for previous line beginning w/ the given mask in the inputLine history
 // Returns NULL if none found
-const char *scr_cmdhisto_prev(char *mask, guint len)
+static const char *scr_cmdhisto_prev(char *mask, guint len)
 {
   GList *hl;
   if (!cmdhisto_cur) {
@@ -1251,7 +1158,7 @@
 //  scr_cmdhisto_next()
 // Look for next line beginning w/ the given mask in the inputLine history
 // Returns NULL if none found
-const char *scr_cmdhisto_next(char *mask, guint len)
+static const char *scr_cmdhisto_next(char *mask, guint len)
 {
   GList *hl;
   if (!cmdhisto_cur) return NULL;
@@ -1328,7 +1235,7 @@
 //  0 -> command
 //  1 -> parameter 1 (etc.)
 //  If > 0, then *p_row is set to the beginning of the row
-int which_row(char **p_row)
+static int which_row(char **p_row)
 {
   int row = -1;
   char *p;
@@ -1361,7 +1268,7 @@
 // Insert the given text at the current cursor position.
 // The cursor is moved.  We don't check if the cursor still is in the screen
 // after, the caller should do that.
-void scr_insert_text(const char *text)
+static void scr_insert_text(const char *text)
 {
   char tmpLine[INPUTLINE_LENGTH+1];
   int len = strlen(text);
@@ -1379,7 +1286,7 @@
 //  scr_handle_tab()
 // Function called when tab is pressed.
 // Initiate or continue a completion...
-void scr_handle_tab(void)
+static void scr_handle_tab(void)
 {
   int nrow;
   char *row;
@@ -1443,7 +1350,7 @@
   }
 }
 
-void scr_cancel_current_completion(void)
+static void scr_cancel_current_completion(void)
 {
   char *c;
   guint back = cancel_completion();
@@ -1454,7 +1361,7 @@
     *c = *(c+back);
 }
 
-void scr_end_current_completion(void)
+static void scr_end_current_completion(void)
 {
   done_completion();
   completion_started = FALSE;
@@ -1463,7 +1370,7 @@
 //  check_offset(int direction)
 // Check inputline_offset value, and make sure the cursor is inside the
 // screen.
-inline void check_offset(int direction)
+static inline void check_offset(int direction)
 {
   // Left side
   if (inputline_offset && direction <= 0) {
@@ -1482,7 +1389,7 @@
   }
 }
 
-inline void refresh_inputline(void)
+static inline void refresh_inputline(void)
 {
   mvwprintw(inputWnd, 0,0, "%s", inputLine + inputline_offset);
   wclrtoeol(inputWnd);
@@ -1555,6 +1462,7 @@
           break;
       case '\n':  // Enter
       case 15:    // Ctrl-o ("accept-line-and-down-history")
+          scr_CheckAutoAway(TRUE);
           if (process_line(inputLine))
             return 255;
           // Add line to history
@@ -1596,9 +1504,11 @@
           }
           break;
       case KEY_PPAGE:
+          scr_CheckAutoAway(TRUE);
           scr_RosterUp();
           break;
       case KEY_NPAGE:
+          scr_CheckAutoAway(TRUE);
           scr_RosterDown();
           break;
       case KEY_HOME:
@@ -1621,12 +1531,13 @@
           *ptr_inputline = 0;
           break;
       case 16:  // Ctrl-p
-          scr_ScrollUp();
+          scr_ScrollUpDown(-1);
           break;
       case 14:  // Ctrl-n
-          scr_ScrollDown();
+          scr_ScrollUpDown(1);
           break;
       case 17:  // Ctrl-q
+          scr_CheckAutoAway(TRUE);
           scr_RosterUnreadMessage(1); // next unread message
           break;
       case 20:  // Ctrl-t
@@ -1636,6 +1547,7 @@
           readline_backward_kill_word();
           break;
       case 27:  // ESC
+          scr_CheckAutoAway(TRUE);
           currentWindow = NULL;
           chatmode = FALSE;
           if (current_buddy)
@@ -1646,6 +1558,7 @@
           break;
       case 12:  // Ctrl-l
       case KEY_RESIZE:
+          scr_CheckAutoAway(TRUE);
           scr_Resize();
           break;
       default:
@@ -1653,6 +1566,7 @@
             const gchar *boundcmd = isbound(key);
             if (boundcmd) {
               gchar *cmd = g_strdup_printf("/%s", boundcmd);
+              scr_CheckAutoAway(TRUE);
               if (process_command(cmd))
                 return 255;
               g_free(cmd);
--- a/mcabber/src/screen.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/screen.h	Sun Jul 24 15:13:53 2005 +0100
@@ -39,19 +39,19 @@
 inline const char *scr_get_multiline();
 void scr_handle_sigint(void);
 
-WINDOW *scr_GetInputWindow(void);
-
 int scr_Getch(void);
 
 int process_key(int);
 
+void scr_CheckAutoAway(bool activity);
+
 // For commands...
 void scr_RosterTop(void);
 void scr_RosterBottom(void);
 void scr_RosterSearch(char *);
-void scr_BufferTop(void);
-void scr_BufferBottom(void);
+void scr_BufferTopBottom(int topbottom);
 void scr_Clear(void);
 void scr_RosterUnreadMessage(int);
+void scr_RosterJumpAlternate(void);
 
 #endif
--- a/mcabber/src/settings.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/settings.c	Sun Jul 24 15:13:53 2005 +0100
@@ -24,6 +24,9 @@
 #include <ctype.h>
 
 #include "settings.h"
+#include "commands.h"
+#include "utils.h"
+#include "screen.h"
 
 static GSList *option;
 static GSList *alias;
@@ -35,7 +38,7 @@
   gchar *value;
 } T_setting;
 
-inline GSList **get_list_ptr(guint type)
+static inline GSList **get_list_ptr(guint type)
 {
   if      (type == SETTINGS_TYPE_OPTION)  return &option;
   else if (type == SETTINGS_TYPE_ALIAS)   return &alias;
@@ -44,7 +47,7 @@
 }
 
 // Return a pointer to the node with the requested key, or NULL if none found
-GSList *settings_find(GSList *list, const gchar *key)
+static GSList *settings_find(GSList *list, const gchar *key)
 {
   GSList *ptr;
   
@@ -59,6 +62,98 @@
 
 /* -- */
 
+//  cfg_read_file(filename)
+// Read and parse config file "filename".  If filename is NULL,
+// try to open the configuration file at the default locations.
+//
+int cfg_read_file(char *filename)
+{
+  FILE *fp;
+  char *buf;
+  char *line, *eol;
+  unsigned int ln = 0;
+  int err = 0;
+
+  if (!filename) {
+    // Use default config file locations
+    char *home = getenv("HOME");
+    if (!home) {
+      ut_WriteLog("Can't find home dir!\n");
+      fprintf(stderr, "Can't find home dir!\n");
+      return -1;
+    }
+    filename = g_new(char, strlen(home)+24);
+    sprintf(filename, "%s/.mcabber/mcabberrc", home);
+    if ((fp = fopen(filename, "r")) == NULL) {
+      // 2nd try...
+      sprintf(filename, "%s/.mcabberrc", home);
+      if ((fp = fopen(filename, "r")) == NULL) {
+        fprintf(stderr, "Cannot open config file!\n");
+        return -1;
+      }
+    }
+    // Check configuration file permissions
+    // As it could contain sensitive data, we make it user-readable only
+    checkset_perm(filename, TRUE);
+    // Check mcabber dir.  There we just warn, we don't change the modes
+    sprintf(filename, "%s/.mcabber/", home);
+    checkset_perm(filename, FALSE);
+    g_free(filename);
+  } else {
+    if ((fp = fopen(filename, "r")) == NULL) {
+      perror("fopen (cfg_file())");
+      return -1;
+    }
+    // Check configuration file permissions (see above)
+    checkset_perm(filename, TRUE);
+  }
+
+  buf = g_new(char, 512);
+
+  while (fgets(buf+1, 511, fp) != NULL) {
+    // The first char is reserved to add a '/', to make a command line
+    line = buf+1;
+    ln++;
+
+    // Strip leading spaces
+    while (isspace(*line))
+      line++;
+
+    // Make eol point to the last char of the line
+    for (eol = line ; *eol ; eol++)
+      ;
+    if (eol > line)
+      eol--;
+
+    // Strip trailing spaces
+    while (eol > line && isspace(*eol))
+      *eol-- = 0;
+
+    // Ignore empty lines and comments
+    if ((*line == '\n') || (*line == '\0') || (*line == '#'))
+      continue;
+
+    if ((strchr(line, '=') != NULL)) {
+      // Only accept the set, alias and bind commands
+      if (strncmp(line, "set ", 4) &&
+          strncmp(line, "bind ", 5) &&
+          strncmp(line, "alias ", 6)) {
+        scr_LogPrint("Error in configuration file (l. %d): bad command", ln);
+        err++;
+        continue;
+      }
+      *(--line) = '/';        // Set the leading '/' to build a command line
+      process_command(line);  // Process the command
+    } else {
+      scr_LogPrint("Error in configuration file (l. %d): no assignment", ln);
+      err++;
+    }
+  }
+  g_free(buf);
+  fclose(fp);
+  return err;
+}
+
 //  parse_assigment(assignment, pkey, pval)
 // Read assignment and split it to key, value
 //
@@ -119,6 +214,12 @@
 
   if (t < val) return TRUE; // no value (variable reset for example)
 
+  // If the value begins and ends with quotes ("), these quotes are
+  // removed and whitespace is not stripped
+  if ((t>val) && (*val == '"' && *t == '"')) {
+    val++;
+    t--;
+  }
   *pval = g_strndup(val, t+1-val);
   return TRUE;
 }
--- a/mcabber/src/settings.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/settings.h	Sun Jul 24 15:13:53 2005 +0100
@@ -16,7 +16,7 @@
 #define MSG_DND       "Busy"
 #define MSG_NOTAVAIL  "Not available"
 #define MSG_AWAY      "Away"
-#define MSG_AUTOAWAY  "Auto away"
+#define MSG_AUTOAWAY  "Auto away status (idle)"
 
 
 #define SETTINGS_TYPE_OPTION    1
@@ -26,6 +26,7 @@
 #define settings_opt_get(k)     settings_get(SETTINGS_TYPE_OPTION, k)
 #define settings_opt_get_int(k) settings_get_int(SETTINGS_TYPE_OPTION, k)
 
+int     cfg_read_file(char *filename);
 guint   parse_assigment(gchar *assignment,
                         const gchar **pkey, const gchar **pval);
 void    settings_set(guint type, const gchar *key, const gchar *value);
--- a/mcabber/src/utf8.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/utf8.c	Sun Jul 24 15:13:53 2005 +0100
@@ -13,8 +13,11 @@
  */
 char *utf8_decode(const char *src)
 {
-  char *ret = calloc(1, strlen(src) + 1);
-  unsigned char *aux = (unsigned char*)ret;
+  unsigned char *ret, *aux;
+  
+  if (!src) return NULL;
+
+  aux = ret = calloc(1, strlen(src) + 1);
 
   while (*src) {
     unsigned char lead = *src++;
@@ -27,7 +30,7 @@
     aux++;
   }
 
-  return ret;
+  return (char*)ret;
 }
 
 
@@ -40,8 +43,11 @@
  */
 char *utf8_encode(const char *src)
 {
-  char *ret = calloc(1, (strlen(src) * 2) + 1);
-  unsigned char *aux = (unsigned char*)ret;
+  unsigned char *ret, *aux;
+  
+  if (!src) return NULL;
+
+  aux = ret = calloc(1, (strlen(src) * 2) + 1);
 
   while (*src) {
     unsigned char ch = *src++;
@@ -53,5 +59,5 @@
     }
   }
 
-  return ret;
+  return (char*)ret;
 }
--- a/mcabber/src/utils.c	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/utils.c	Sun Jul 24 15:13:53 2005 +0100
@@ -26,8 +26,12 @@
 #include <string.h>
 #include <stdarg.h>
 #include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <config.h>
+#include "screen.h"
 
 static int DebugEnabled;
 static char *FName;
@@ -90,6 +94,45 @@
   }
 }
 
+//  checkset_perm(name, setmode)
+// Check the permissions of the "name" file/dir
+// If setmode is true, correct the permissions if they are wrong
+// Return values: -1 == bad file/dir, 0 == success, 1 == cannot correct
+int checkset_perm(const char *name, unsigned int setmode)
+{
+  int fd;
+  struct stat buf;
+
+  fd = lstat(name, &buf);
+  if (fd == -1) return -1;
+
+  if (buf.st_uid != geteuid()) {
+    scr_LogPrint("Wrong file owner [%s]", name);
+    return 1;
+  }
+
+  if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP) ||
+      buf.st_mode & (S_IROTH | S_IWOTH | S_IXOTH)) {
+    if (setmode) {
+      mode_t newmode = 0;
+      scr_LogPrint("Bad permissions [%s]", name);
+      if (S_ISDIR(buf.st_mode))
+        newmode |= S_IXUSR;
+      newmode |= S_IRUSR | S_IWUSR;
+      if (chmod(name, newmode)) {
+        scr_LogPrint("WARNING: Failed to correct permissions!");
+        return 1;
+      }
+      scr_LogPrint("Permissions have been corrected");
+    } else {
+      scr_LogPrint("WARNING: Bad permissions [%s]", name);
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
 //  to_iso8601(dststr, timestamp)
 // Convert timestamp to iso8601 format, and store it in dststr.
 // NOTE: dststr should be at last 19 chars long.
@@ -185,4 +228,3 @@
 
   return retval;
 }
-
--- a/mcabber/src/utils.h	Fri Jul 15 10:56:15 2005 +0100
+++ b/mcabber/src/utils.h	Sun Jul 24 15:13:53 2005 +0100
@@ -4,6 +4,8 @@
 void ut_InitDebug(unsigned int level, const char *file);
 void ut_WriteLog(const char *fmt, ...);
 
+int checkset_perm(const char *name, unsigned int setmode);
+
 int    to_iso8601(char *dststr, time_t timestamp);
 time_t from_iso8601(const char *timestamp, int utc);