]> arthur.barton.de Git - netatalk.git/commitdiff
Merge branch 'v3-cleanup' into tmp/v3.0.4-alex tmp/v3.0.4-alex
authorAlexander Barton <alex@barton.de>
Sat, 20 Jul 2013 10:12:21 +0000 (12:12 +0200)
committerAlexander Barton <alex@barton.de>
Sat, 20 Jul 2013 10:12:21 +0000 (12:12 +0200)
* v3-cleanup:
  autoconf: Indentation fixes
  Spelling fixes
  Whitespace and exclamation mark fixes

Conflicts:
etc/afpd/main.c

340 files changed:
Makefile.am
NEWS
VERSION
bin/ad/ad.c
bin/ad/ad_cp.c
bin/ad/ad_ls.c
bin/ad/ad_mv.c
bin/ad/ad_set.c
bin/ad/ad_util.c
bin/cnid/cnid2_create.in
bin/megatron/asingle.c
bin/megatron/asingle.h
bin/megatron/hqx.c
bin/megatron/hqx.h
bin/megatron/macbin.c
bin/megatron/macbin.h
bin/megatron/megatron.h
bin/megatron/nad.c
bin/megatron/nad.h
bin/megatron/updcrc.c
bin/megatron/updcrc.h
bin/misc/fce.c
bin/misc/logger_test.c
bin/misc/netacnv.c
bootstrap
config/Makefile.am
config/extmap.conf [new file with mode: 0644]
config/netatalk-dbus.conf [new file with mode: 0644]
config/pam/.gitignore
config/pam/Makefile.am
config/pam/netatalk.pam.tmpl [deleted file]
config/pam/netatalk.tmpl [new file with mode: 0644]
configure.ac
contrib/misc/libevent.patch
contrib/shell_utils/Makefile.am
contrib/shell_utils/afpstats [new file with mode: 0755]
contrib/shell_utils/apple_dump.in
distrib/initscripts/Makefile.am
distrib/initscripts/rc.redhat.tmpl
distrib/initscripts/service.systemd.tmpl
doc/.gitignore
doc/DEVELOPER
doc/Makefile.am
doc/gfx_and_css/logo.ai [new file with mode: 0644]
doc/gfx_and_css/logo.pdf [new file with mode: 0644]
doc/gfx_and_css/logo.png [new file with mode: 0644]
doc/gfx_and_css/netatalk.css [new file with mode: 0644]
doc/html.xsl.in [new file with mode: 0644]
doc/man.xsl.in [new file with mode: 0644]
doc/manpages/Makefile.am [new file with mode: 0644]
doc/manpages/man1/.gitignore [new file with mode: 0644]
doc/manpages/man1/Makefile.am [new file with mode: 0644]
doc/manpages/man1/ad.1.xml [new file with mode: 0644]
doc/manpages/man1/afpldaptest.1.xml [new file with mode: 0644]
doc/manpages/man1/afppasswd.1.xml [new file with mode: 0644]
doc/manpages/man1/afpstats.1.xml [new file with mode: 0644]
doc/manpages/man1/apple_dump.1.xml [new file with mode: 0644]
doc/manpages/man1/asip-status.pl.1.xml [new file with mode: 0644]
doc/manpages/man1/dbd.1.xml [new file with mode: 0644]
doc/manpages/man1/macusers.1.xml [new file with mode: 0644]
doc/manpages/man1/megatron.1.xml [new file with mode: 0644]
doc/manpages/man1/netatalk-config.1.xml [new file with mode: 0644]
doc/manpages/man1/uniconv.1.xml [new file with mode: 0644]
doc/manpages/man5/.gitignore [new file with mode: 0644]
doc/manpages/man5/Makefile.am [new file with mode: 0644]
doc/manpages/man5/afp.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/afp_signature.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/afp_voluuid.conf.5.xml [new file with mode: 0644]
doc/manpages/man5/extmap.conf.5.xml [new file with mode: 0644]
doc/manpages/man8/.gitignore [new file with mode: 0644]
doc/manpages/man8/Makefile.am [new file with mode: 0644]
doc/manpages/man8/afpd.8.xml [new file with mode: 0644]
doc/manpages/man8/cnid_dbd.8.xml [new file with mode: 0644]
doc/manpages/man8/cnid_metad.8.xml [new file with mode: 0644]
doc/manpages/man8/netatalk.8.xml [new file with mode: 0644]
doc/manual/.gitignore [new file with mode: 0644]
doc/manual/Makefile.am [new file with mode: 0644]
doc/manual/configuration.xml [new file with mode: 0644]
doc/manual/install.xml [new file with mode: 0644]
doc/manual/intro.xml [new file with mode: 0644]
doc/manual/manual.xml.in [new file with mode: 0644]
doc/manual/upgrade.xml [new file with mode: 0644]
doc/netatalk.html [new file with mode: 0644]
doc/www/ReleaseNotes [new file with mode: 0644]
doc/www/asciidoc.conf [new file with mode: 0644]
doc/www/asciidoc.py [new file with mode: 0755]
doc/www/create-relnotes.sh [new file with mode: 0755]
doc/www/html5.conf [new file with mode: 0644]
doc/www/javascripts/asciidoc.js [new file with mode: 0644]
doc/www/lang-en.conf [new file with mode: 0644]
doc/www/netatalk-relnotes.conf [new file with mode: 0644]
doc/www/stylesheets/asciidoc.css [new file with mode: 0644]
etc/afpd/Makefile.am
etc/afpd/acls.c
etc/afpd/afp_config.c
etc/afpd/afp_dsi.c
etc/afpd/afp_mdns.c
etc/afpd/afp_options.c
etc/afpd/afp_util.c
etc/afpd/afpstats-service.xml [new file with mode: 0644]
etc/afpd/afpstats.c [new file with mode: 0644]
etc/afpd/afpstats.h [new file with mode: 0644]
etc/afpd/afpstats_obj.c [new file with mode: 0644]
etc/afpd/afpstats_obj.h [new file with mode: 0644]
etc/afpd/afpstats_service_glue.h [new file with mode: 0644]
etc/afpd/afs.c
etc/afpd/appl.c
etc/afpd/auth.c
etc/afpd/catsearch.c
etc/afpd/desktop.c
etc/afpd/dircache.c
etc/afpd/directory.c
etc/afpd/directory.h
etc/afpd/enumerate.c
etc/afpd/extattrs.c
etc/afpd/extattrs.h
etc/afpd/fce_api.c
etc/afpd/fce_api_internal.h
etc/afpd/fce_util.c
etc/afpd/file.c
etc/afpd/file.h
etc/afpd/filedir.c
etc/afpd/fork.c
etc/afpd/fork.h
etc/afpd/gettok.c [deleted file]
etc/afpd/hash.c
etc/afpd/hash.h
etc/afpd/main.c
etc/afpd/mangle.h
etc/afpd/nfsquota.c
etc/afpd/ofork.c
etc/afpd/quota.c
etc/afpd/status.c
etc/afpd/switch.h
etc/afpd/uam.c
etc/afpd/uid.c
etc/afpd/uid.h
etc/afpd/unix.c
etc/afpd/unix.h
etc/afpd/volume.c
etc/afpd/volume.h
etc/cnid_dbd/cmd_dbd.c
etc/cnid_dbd/cmd_dbd.h
etc/cnid_dbd/cmd_dbd_scanvol.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/db_param.c
etc/cnid_dbd/dbd.h
etc/cnid_dbd/dbd_add.c
etc/cnid_dbd/dbd_dbcheck.c
etc/cnid_dbd/dbd_delete.c
etc/cnid_dbd/dbd_get.c
etc/cnid_dbd/dbd_getstamp.c
etc/cnid_dbd/dbd_lookup.c
etc/cnid_dbd/dbd_rebuild_add.c
etc/cnid_dbd/dbd_resolve.c
etc/cnid_dbd/dbif.c
etc/cnid_dbd/dbif.h
etc/cnid_dbd/main.c
etc/cnid_dbd/usockfd.h
etc/netatalk/netatalk.c
etc/uams/uams_dhx2_pam.c
etc/uams/uams_dhx2_passwd.c
etc/uams/uams_dhx_pam.c
etc/uams/uams_gss.c
etc/uams/uams_guest.c
etc/uams/uams_pam.c
etc/uams/uams_pgp.c
etc/uams/uams_randnum.c
include/atalk/.gitignore
include/atalk/Makefile.am
include/atalk/acl.h
include/atalk/adouble.h
include/atalk/afp_dtrace.d [new file with mode: 0644]
include/atalk/cnid.h
include/atalk/cnid_dbd_private.h
include/atalk/dictionary.h
include/atalk/dsi.h
include/atalk/errchk.h
include/atalk/fce_api.h
include/atalk/globals.h
include/atalk/hash.h
include/atalk/iniparser.h
include/atalk/ldapconfig.h
include/atalk/logger.h
include/atalk/netatalk_conf.h
include/atalk/paths.h
include/atalk/server_child.h
include/atalk/server_ipc.h
include/atalk/unicode.h
include/atalk/unix.h
include/atalk/util.h
include/atalk/volume.h
libatalk/.gitignore
libatalk/Makefile.am
libatalk/acl/Makefile.am
libatalk/acl/cache.c
libatalk/acl/cache.h
libatalk/acl/ldap.c
libatalk/acl/ldap_config.c
libatalk/acl/unix.c
libatalk/acl/uuid.c
libatalk/adouble/ad_attr.c
libatalk/adouble/ad_conv.c
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_lock.c
libatalk/adouble/ad_open.c
libatalk/adouble/ad_read.c
libatalk/adouble/ad_size.c
libatalk/adouble/ad_write.c
libatalk/bstring/bstradd.c
libatalk/cnid/cdb/cnid_cdb_close.c
libatalk/cnid/cdb/cnid_cdb_delete.c
libatalk/cnid/cdb/cnid_cdb_meta.c
libatalk/cnid/cdb/cnid_cdb_meta.h
libatalk/cnid/cdb/cnid_cdb_nextid.c
libatalk/cnid/cdb/cnid_cdb_open.c
libatalk/cnid/cdb/cnid_cdb_rebuild_add.c
libatalk/cnid/cdb/cnid_cdb_resolve.c
libatalk/cnid/cnid.c
libatalk/cnid/cnid_init.c
libatalk/cnid/dbd/cnid_dbd.c
libatalk/cnid/dbd/cnid_dbd.h
libatalk/cnid/last/cnid_last.c
libatalk/cnid/tdb/cnid_tdb_add.c
libatalk/cnid/tdb/cnid_tdb_close.c
libatalk/cnid/tdb/cnid_tdb_delete.c
libatalk/cnid/tdb/cnid_tdb_get.c
libatalk/cnid/tdb/cnid_tdb_lookup.c
libatalk/cnid/tdb/cnid_tdb_nextid.c
libatalk/cnid/tdb/cnid_tdb_open.c
libatalk/cnid/tdb/cnid_tdb_resolve.c
libatalk/cnid/tdb/cnid_tdb_update.c
libatalk/compat/getusershell.c
libatalk/compat/mktemp.c
libatalk/compat/rquota_xdr.c
libatalk/dsi/dsi_attn.c
libatalk/dsi/dsi_close.c
libatalk/dsi/dsi_getsess.c
libatalk/dsi/dsi_getstat.c
libatalk/dsi/dsi_stream.c
libatalk/dsi/dsi_tcp.c
libatalk/dsi/dsi_tickle.c
libatalk/dsi/dsi_write.c
libatalk/iniparser/dictionary.c
libatalk/iniparser/iniparser.c
libatalk/libatalk-3.0.1dev.abi [new file with mode: 0644]
libatalk/libatalk-3.0.2.abi [new file with mode: 0644]
libatalk/libatalk-3.0.3.abi [new file with mode: 0644]
libatalk/libatalk-3.0.4.abi [new file with mode: 0644]
libatalk/tdb/io.c
libatalk/tdb/tdb_private.h
libatalk/unicode/charcnv.c
libatalk/unicode/charsets/mac_roman.h
libatalk/unicode/util_unistr.c
libatalk/util/Makefile.am
libatalk/util/ftw.c
libatalk/util/gettok.c [new file with mode: 0644]
libatalk/util/locking.c
libatalk/util/logger.c
libatalk/util/module.c
libatalk/util/netatalk_conf.c
libatalk/util/server_child.c
libatalk/util/server_ipc.c
libatalk/util/server_lock.c
libatalk/util/socket.c
libatalk/util/unix.c
libatalk/vfs/acl.c
libatalk/vfs/ea_ad.c
libatalk/vfs/ea_sys.c
libatalk/vfs/extattr.c
libatalk/vfs/unix.c
libatalk/vfs/vfs.c
libevent/Makefile.am
macros/Makefile.am
macros/afs-check.m4
macros/ax_pthread.m4 [new file with mode: 0644]
macros/grep-check.m4
macros/netatalk.m4
macros/pam-check.m4
macros/perl-check.m4
macros/ps-check.m4
macros/quota-check.m4
macros/ssl-check.m4
macros/summary.m4
macros/tcp-wrappers.m4
man/.gitignore
man/man1/.gitignore
man/man1/Makefile.am
man/man1/ad.1 [deleted file]
man/man1/ad.1.in [new file with mode: 0644]
man/man1/afpldaptest.1.in [new file with mode: 0644]
man/man1/afpldaptest.1.tmpl [deleted file]
man/man1/afppasswd.1 [deleted file]
man/man1/afppasswd.1.in [new file with mode: 0644]
man/man1/afpstats.1.in [new file with mode: 0644]
man/man1/apple_dump.1 [deleted file]
man/man1/apple_dump.1.in [new file with mode: 0644]
man/man1/asip-status.pl.1.in [new file with mode: 0644]
man/man1/asip-status.pl.1.tmpl [deleted file]
man/man1/dbd.1 [deleted file]
man/man1/dbd.1.in [new file with mode: 0644]
man/man1/hqx2bin.1
man/man1/macbinary.1
man/man1/macusers.1 [deleted file]
man/man1/macusers.1.in [new file with mode: 0644]
man/man1/megatron.1 [deleted file]
man/man1/megatron.1.in [new file with mode: 0644]
man/man1/netatalk-config.1 [deleted file]
man/man1/netatalk-config.1.in [new file with mode: 0644]
man/man1/single2bin.1
man/man1/unbin.1
man/man1/unhex.1
man/man1/uniconv.1.in [new file with mode: 0644]
man/man1/uniconv.1.tmpl [deleted file]
man/man1/unsingle.1
man/man5/.gitignore
man/man5/Makefile.am
man/man5/afp.conf.5.in [new file with mode: 0644]
man/man5/afp.conf.5.tmpl [deleted file]
man/man5/afp_signature.conf.5.in [new file with mode: 0644]
man/man5/afp_signature.conf.5.tmpl [deleted file]
man/man5/afp_voluuid.conf.5.in [new file with mode: 0644]
man/man5/afp_voluuid.conf.5.tmpl [deleted file]
man/man5/extmap.conf.5.in [new file with mode: 0644]
man/man8/.gitignore
man/man8/Makefile.am
man/man8/afpd.8.in [new file with mode: 0644]
man/man8/afpd.8.tmpl [deleted file]
man/man8/cnid_dbd.8.in [new file with mode: 0644]
man/man8/cnid_dbd.8.tmpl [deleted file]
man/man8/cnid_metad.8.in [new file with mode: 0644]
man/man8/cnid_metad.8.tmpl [deleted file]
man/man8/netatalk.8.in [new file with mode: 0644]
man/man8/netatalk.8.tmpl [deleted file]
test/afpd/Makefile.am
test/afpd/afpfunc_helpers.c
test/afpd/afpfunc_helpers.h
test/afpd/subtests.h
test/afpd/test.c
test/afpd/test.h

index 0100598597364a3d8483a2686979afb9e1ad1d29..4d714f2de666a4451205a3f4bf7657e29334a0bb 100644 (file)
@@ -1,9 +1,9 @@
 # Makefile.am for top level of netatalk package
 
 if USE_BUILTIN_LIBEVENT
-SUBDIRS = libevent libatalk bin config etc man contrib distrib include doc macros test
+SUBDIRS = libevent include libatalk bin config etc contrib distrib doc man macros test
 else
-SUBDIRS = libatalk bin config etc man contrib distrib include doc macros test
+SUBDIRS = include libatalk bin config etc contrib distrib doc man macros test
 endif
 
 EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS VERSION
diff --git a/NEWS b/NEWS
index 866ac8871b478afb263850cbd13976e718f39c30..cb54cd87bc2fefa96ac9f1b15804e25dff9528fa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,113 @@
+Changes in 3.0.4
+================
+* FIX: Opening files without metadata EA may result in an invalid
+       metadata EA. Check for malformed metadata EAs and delete them.
+       Fixes bug #510.
+* FIX: Fix an issue with filenames containing non-ASCII characters that
+       lead to a failure setting the size of a files ressource fork.
+       This affected application like Adobe Photoshop where saving
+       files may fail. Fixes bug #511.
+* UPD: Enhance ACL mapping, change global ACL option 'map acl' to take
+       the following options: "none", "rights" (default), "mode".
+       none   = no mapping, this resembles the previous false/no setting
+       rights = map ACLs to Finder UARights, this resembles the previous
+                true/yes setting. This is the default.
+       mode   = map ACLs to Finder UARights and UNIX mode
+       From FR #73.
+* FIX: Fix a possible crash in cname() where cname_mtouname calls
+       dirlookup() where the curdir is freed because the dircache
+       detected a dev/inode cache difference and evicted the object
+       from the cache. Fixes bug #498.
+* FIX: Add missing include, fixes bug #512.
+* FIX: Change default FinderInfo for directories to be all 0, fixes
+       bug 514.
+* NEW: New option "afp interfaces" which allows specifying where
+       Netatalk listens for AFP connections by interface names.
+       From FR #79.
+
+Changes in 3.0.3
+================
+* UPD: afpd: Increase default DSI server quantum to 1 MB
+* UPD: bundled libevent2 is now static
+* NEW: --with-lockfile=PATH configure option for specifying an
+       alternative path for the netatalk lockfile.
+* UPD: systemd service file use PIDFile and ExecReload.
+       From FR #70.
+* UPD: RedHat sysvinit: rm graceful, reimplement reload, add condrestart
+* FIX: Couldn't create folders on FreeBSD 9.1 ZFS fileystems.
+       Fixed bug #491.
+* FIX: Fix an issue with user homes when user home directory has not the
+       same name as the username.
+       Fixes bug #497.
+* UPD: Fix PAM config install, new default installation dir is
+       $sysconfdir/pam.d/. Add configure option --with-pam-confdir
+       to specify alternative path.
+* NEW: AFP stats about active session via dbus IPC. Client side python
+       program `afpstats`. Requires dbus, dbus-glib any python-dbus.
+       configure option --dbus-sysconf-dir for specifying dbus
+       system security configuration files.
+       New option 'afpstats' (default: no) which determines whether
+       to enable the feature or not.
+* NEW: configure option --with-init-dir
+* NEW: dtrace probes, cf include/atalk/afp_dtrace.d for available
+       probes.
+* UPD: Reload groups when reloading volumes. FR #71.
+* FIX: Attempt to read read-only ._ rfork results in disconnect.
+       Fixes bug #502.
+* FIX: File's ressource fork can't be read if metadata EA is missing.
+       Fixes bug #501.
+* FIX: Conversion from adouble v2 to ea for directories.
+       Fixes bug #500.
+* FIX: Error messages when mounting read-only filesystems.
+       Fixes bug #504.
+* FIX: Permissions of ._ AppleDouble ressource fork after conversion
+       from v2 to ea.
+       Fixes bug #505.
+* UPD: Use FreeBSD sendfile() capability to send protocol header.
+       From FR #75.
+* UPD: Increase IO size when sendfile() is not used.
+       From FR #76.
+* FIX: Can't set Finder label on symlinked folder with "follow symlinks = yes".
+       Fixes bug #508.
+* FIX: Setting POSIX ACLs on Linux
+       Fixes bug #506.
+* FIX: "ad ls" segfault if requested object is not in an AFP volume.
+       Fixes bug #496.
+
+Changes in 3.0.2
+================
+* NEW: afpd: Put file extension type/creator mapping back in which had
+       been removed in 3.0.
+* NEW: afpd: new option 'ad domain'. From FR #66.
+* FIX: volumes and home share with symlinks in the path
+* FIX: Copying packages to a Netatalk share could fail, bug #469
+* FIX: Reloading volumes from config file was broken.  Fixes bug #474.
+* FIX: Fix _device-info service type registered with dns-sd API
+* FIX: Fix pathname bug for FCE modified event.
+* FIX: Remove length limitation of options like "valid users".
+       Fixes bug #473.
+* FIX: Dont copy our metadata EA in copyfile(). Fixes bug #452.
+* FIX: Fix an error where catalog search gave incomplete results.
+       Fixes bug #479.
+* REM: Remove TimeMachine volume used size FCE event.
+* UPD: Add quoting support to '[in]valid users' option. Fixes bug #472.
+* FIX: Install working PAM config on Solaris 11. Fixes bug #481.
+* FIX: Fix a race condition between dbd and the cnid_dbd daemon
+       which could result in users being disconnected from volumes
+       when dbd was scanning their volumes. Fixes bug #477.
+* FIX: Netatalk didn't start when the last line of the config file
+       afp.conf wasn't terminated by a newline. Fixes bug #476.
+* NEW: Add a new volumes option 'follow symlinks'. The default setting is
+       false, symlinks are not followed on the server. This is the same
+       behaviour as OS X's AFP server.
+       Setting the option to true causes afpd to follow symlinks on the
+       server. symlinks may point outside of the AFP volume, currently
+       afpd doesn't do any checks for "wide symlinks".
+* FIX: Automatic AppleDouble conversion to EAs failing for directories.
+       Fixes bug #486.
+* FIX: dbd failed to convert appledouble files of symlinks.
+       Fixes bug #490.
+
 Changes in 3.0.1
 ================
 * NEW: afpd: Optional "ldap uuid encoding = string | ms-guid" parameter to
diff --git a/VERSION b/VERSION
index 13d683ccbfeed4ecf19a8f76e016a4e0296ea4cf..b38ebbfce26aa93c94f0743a690abe4929d6b5e1 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.1
\ No newline at end of file
+3.0.4
\ No newline at end of file
index 7f5b3da6260e626ae210dfa71c82e13777a55240..3de3030b9a820bbde3c53caef74a27ac1b769482 100644 (file)
@@ -58,7 +58,7 @@ int main(int argc, char **argv)
 
     setuplog("default:note", "/dev/tty");
 
-    if (load_volumes(&obj, NULL) != 0)
+    if (load_volumes(&obj) != 0)
         return 1;
 
     if (STRCMP(argv[1], ==, "ls"))
index ef995ce8cf3f159c6346a5e32a4858e9cfad8948..239936dc06940cb788a62fd198e60f6c0051878b 100644 (file)
@@ -95,36 +95,14 @@ static volatile sig_atomic_t alarmed;
 static int badcp, rval;
 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
 
-static char           *netatalk_dirs[] = {
-    ".AppleDouble",
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
-
 /* Forward declarations */
 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
 static int setfile(const struct stat *, int);
-static int preserve_dir_acls(const struct stat *, char *, char *);
+// static int preserve_dir_acls(const struct stat *, char *, char *);
 static int preserve_fd_acls(int, int);
 
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
-
 static void upfunc(void)
 {
     did = pdid;
@@ -953,9 +931,9 @@ static int preserve_fd_acls(int source_fd, int dest_fd)
     return (0);
 }
 
+#if 0
 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
 {
-#if 0
     acl_t (*aclgetf)(const char *, acl_type_t);
     int (*aclsetf)(const char *, acl_type_t, acl_t);
     struct acl *aclp;
@@ -1037,6 +1015,6 @@ static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest
         return (1);
     }
     acl_free(acl);
-#endif
     return (0);
 }
+#endif
index d8362595e4647a7ef54098d4f1711b5b45b5b2ce..b5947e17dd0b48c3fa7d12e477128989372fe38d 100644 (file)
@@ -227,7 +227,7 @@ static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
     if (S_ISDIR(st->st_mode))
         adflags = ADFLAGS_DIR;
 
-    if (vol->vol->v_path == NULL)
+    if (vol->vol == NULL || vol->vol->v_path == NULL)
         return;
 
     ad_init(&ad, vol->vol);
index 7f592dd27e1aaf0623bb2a2e34038e13a6468b33..72698f73e9ad363e2ee5495d0596c77d19144fc2 100644 (file)
@@ -50,31 +50,9 @@ static int fflg, iflg, nflg, vflg;
 static afpvol_t svolume, dvolume;
 static cnid_t did, pdid;
 static volatile sig_atomic_t alarmed;
-static char           *netatalk_dirs[] = {
-    ".AppleDouble",
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
 
 static int copy(const char *, const char *);
 static int do_move(const char *, const char *);
-static void preserve_fd_acls(int source_fd, int dest_fd, const char *source_path,
-                             const char *dest_path);
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
 
 /*
   SIGNAL handling:
@@ -242,7 +220,6 @@ int ad_mv(int argc, char *argv[], AFPObj *obj)
         }
     }
 
-exit:
     closevol(&dvolume);
     return rval;
 }
@@ -465,12 +442,3 @@ static int copy(const char *from, const char *to)
     }
     return 0;
 }
-
-static void
-preserve_fd_acls(int source_fd,
-                 int dest_fd,
-                 const char *source_path,
-                 const char *dest_path)
-{
-    ;
-}
index d41d5222040d8dce083e9e191d0a8807c71db990..af825093c1de8c2709955b4c9c57944db8124976 100644 (file)
@@ -140,7 +140,6 @@ static void change_label(char *path, afpvol_t *vol, const struct stat *st, struc
 
 static void change_attributes(char *path, afpvol_t *vol, const struct stat *st, struct adouble *ad, char *new_attributes)
 {
-    char *FinderInfo;
     uint16_t AFPattributes;
 
     ad_getattr(ad, &AFPattributes);
@@ -256,7 +255,7 @@ static void change_flags(char *path, afpvol_t *vol, const struct stat *st, struc
 
 int ad_set(int argc, char **argv, AFPObj *obj)
 {
-    int c, firstarg;
+    int c;
     afpvol_t vol;
     struct stat st;
     int adflags = 0;
index 897bdf3b1415c609efdae24f2104d019ef3464fc..fbe85235c2af51201ec331e5e0ea67d27ed93f86 100644 (file)
@@ -138,9 +138,11 @@ int openvol(AFPObj *obj, const char *path, afpvol_t *vol)
 
 void closevol(afpvol_t *vol)
 {
-    if (vol->vol->v_cdb) {
-        cnid_close(vol->vol->v_cdb);
-        vol->vol->v_cdb = NULL;
+    if (vol->vol) {
+        if (vol->vol->v_cdb) {
+            cnid_close(vol->vol->v_cdb);
+            vol->vol->v_cdb = NULL;
+        }
     }
     memset(vol, 0, sizeof(afpvol_t));
 }
index e81ff1f3e320be0b965f29225b69b04fd4516967..c3e3ffd6c30a67e44a619c2d10ecf9529edc6ae5 100755 (executable)
@@ -3,7 +3,6 @@
 #
 # Upgrade version 1 CNID databases to version 2
 #
-# $Id: cnid2_create.in,v 1.2 2005-04-28 20:49:19 bfernhomberg Exp $
 #
 # Copyright (C) Joerg Lenneis 2003
 # All Rights Reserved.  See COPYING.
index 73e4ffaaff1022e6bc60a160ae2d550338b56d17..9ba3918b6a0a2dfa65b7d6de7b6e022003e74f50 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: asingle.c,v 1.14 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index c041ccaa99c9875844bdda6e98387563f4c0b9a4..769d58cd2ede563e086a409ce0ea999a2a88d432 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: asingle.h,v 1.4 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _ASINGLE_H
index da2f71cf58f04f7c25d6a0851fd8d8659ff78605..923ddbd1950131c30e98a87a5e078ff230639d7b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: hqx.c,v 1.18 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 38730d055ef4fbf96b4658279b35f04e8f65b6f8..b656f0b63e0c8f5ded4b9a9a0403c83f331a6f9d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: hqx.h,v 1.3 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _HQX_H
index 51b7ac5f616ede35aeb604892e423e3bbf4a35e6..986aac9247c91f6e7cb210030facfa4d566ef01d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: macbin.c,v 1.15 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index d216b1b9fcae4c5fa50c5f940007f42576218255..a1c5a3a1e83aac017b1e0cb904adb457293aba02 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: macbin.h,v 1.4 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _MACBIN_H
index c9aa8cee547ba75916a9b611e3fdb2c8aaa840d3..421a26afc31040c207c13f3d26137922e75dbdce 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: megatron.h,v 1.5 2009-10-14 01:38:28 didg Exp $
  */
 
 #ifndef _MEGATRON_H
index 9d0b64bf5627612a6bfeb1502d0512b6b8d27f1a..abe4541c71cc2f334fe38404ac308179e66610db 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nad.c,v 1.18 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 9fa8568442fc8a9680f353cf568d8aba3404df3c..f69e73231571d4b476e8cb69eeefe6416d8bfec6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nad.h,v 1.5 2010-01-27 21:27:53 didg Exp $
  */
 
 #ifndef _NAD_H
index d327571dc20d6cc41f9ca215082564403da036ae..ca199c8a8d26fe87c126bda0ab2eacd0d008150e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: updcrc.c,v 1.5 2009-10-13 22:55:36 didg Exp $
  *
  * updcrc(3), crc(1) - calculate crc polynomials
  *
index 0b6039e5ff7ccf45e9763b515cde05b4b890efbf..75c38388bd7d5dcfc522e429e8afe050c230dd34 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: updcrc.h,v 1.1 2009-10-13 22:55:36 didg Exp $
  */
 
 #ifndef _UPDCRC_H
index d69699079ff99c63df589f5450fbddc226f3ee3f..23101bfbb34ed7a1622c488f82a1fa4e58278c88 100644 (file)
@@ -27,20 +27,9 @@ static char *fce_ev_names[] = {
     "FCE_FILE_DELETE",
     "FCE_DIR_DELETE",
     "FCE_FILE_CREATE",
-    "FCE_DIR_CREATE",
-    "FCE_TM_SIZE"
+    "FCE_DIR_CREATE"
 };
 
-// get sockaddr, IPv4 or IPv6:
-static void *get_in_addr(struct sockaddr *sa)
-{
-    if (sa->sa_family == AF_INET) {
-        return &(((struct sockaddr_in*)sa)->sin_addr);
-    }
-
-    return &(((struct sockaddr_in6*)sa)->sin6_addr);
-}
-
 static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
 {
     unsigned char *p = buf;
@@ -63,6 +52,7 @@ static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
     packet->datalen = ntohs(packet->datalen);
 
     memcpy(&packet->data[0], p, packet->datalen);
+    packet->data[packet->datalen] = 0; /* 0 terminate strings */
     p += packet->datalen;
 
     return 0;
@@ -77,8 +67,6 @@ int main(void)
     struct sockaddr_storage their_addr;
     char buf[MAXBUFLEN];
     socklen_t addr_len;
-    char s[INET6_ADDRSTRLEN];
-    uint64_t tmsize;
 
     memset(&hints, 0, sizeof hints);
     hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
@@ -129,18 +117,11 @@ int main(void)
             exit(1);
         }
 
-        unpack_fce_packet(buf, &packet);
+        unpack_fce_packet((unsigned char *)buf, &packet);
 
         if (memcmp(packet.magic, FCE_PACKET_MAGIC, sizeof(packet.magic)) == 0) {
 
             switch (packet.mode) {
-            case FCE_TM_SIZE:
-                memcpy(&tmsize, packet.data, sizeof(uint64_t));
-                tmsize = ntoh64(tmsize);
-                printf("ID: %" PRIu32 ", Event: %s, Volume: %s, TM used size: %" PRIu64 " \n",
-                       packet.event_id, fce_ev_names[packet.mode], packet.data + sizeof(uint64_t), tmsize);
-                break;
-
             case FCE_CONN_START:
                 printf("FCE Start\n");
                 break;
index 585c15839158a61534c96aafb62258908308d4ef..cf9321142371817b50d47adab5e42f0542e377a2 100644 (file)
@@ -1,3 +1,21 @@
+/*
+  Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <stdio.h>
 #include <stdbool.h>
 
index e42c76b526b36013ace3c53f993ee2be8efb90f9..70c822d80419e97d342cc9ee6e0ae57719de8bf2 100644 (file)
@@ -21,7 +21,6 @@ struct flag_map {
 
 struct flag_map flag_map[] = {
     flag(CONV_ESCAPEHEX),
-    flag(CONV_ALLOW_COLON),
     flag(CONV_UNESCAPEHEX),    
     flag(CONV_ESCAPEDOTS),
     flag(CONV_IGNORE),
@@ -39,29 +38,32 @@ int main(int argc, char **argv)
 {
     int opt;
     uint16_t flags = 0;
-    char *string;
+    char *string, *macName = MACCHARSET;
     char *f = NULL, *t = NULL;
     charset_t from, to, mac;
 
-    while ((opt = getopt(argc, argv, ":o:f:t:")) != -1) {
+    while ((opt = getopt(argc, argv, "m:o:f:t:")) != -1) {
         switch(opt) {
+        case 'm':
+            macName = strdup(optarg);
+            break;
         case 'o':
             for (int i = 0; i < sizeof(flag_map)/sizeof(struct flag_map) - 1; i++)
                 if ((strcmp(flag_map[i].flagname, optarg)) == 0)
                     flags |= flag_map[i].flag;
             break;
         case 'f':
-            f = optarg;
+            f = strdup(optarg);
             break;
         case 't':
-            t = optarg;
+            t = strdup(optarg);
             break;
         }
     }
 
     if ((optind + 1) != argc) {
-        printf("Usage: test [-o <conversion option> [...]] [-f <from charset>] [-t <to charset>] <string>\n");
-        printf("Defaults: -f: UTF8-MAC , -t: UTF8 \n");
+        printf("Usage: test [-o <conversion option> [...]] [-f <from charset>] [-t <to charset>] [-m legacy Mac charset] <string>\n");
+        printf("Defaults: -f: UTF8-MAC, -t: UTF8, -m MAC_ROMAN\n");
         printf("Available conversion options:\n");
         for (int i = 0; i < (sizeof(flag_map)/sizeof(struct flag_map) - 1); i++) {
             printf("%s\n", flag_map[i].flagname);
@@ -70,6 +72,9 @@ int main(int argc, char **argv)
     }
     string = argv[optind];
 
+    set_charset_name(CH_UNIX, "UTF8");
+    set_charset_name(CH_MAC, macName);
+
     if ( (charset_t) -1 == (from = add_charset(f ? f : "UTF8-MAC")) ) {
         fprintf( stderr, "Setting codepage %s as from codepage failed\n", f ? f : "UTF8-MAC");
         return (-1);
@@ -80,7 +85,7 @@ int main(int argc, char **argv)
         return (-1);
     }
 
-    if ( (charset_t) -1 == (mac = add_charset(MACCHARSET)) ) {
+    if ( (charset_t) -1 == (mac = add_charset(macName)) ) {
         fprintf( stderr, "Setting codepage %s as Mac codepage failed\n", MACCHARSET);
         return (-1);
     }
index 76b98f3b45993438dce5160bcf203d46c4541953..b847ba65e9f23fd8d3a8fd26c25a6db43f15ca7f 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -1,5 +1,50 @@
 #!/bin/sh
 
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`autoconf' installed to."
+  echo "Download the appropriate package for your distribution,"
+  echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+}
+
+(libtool --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`libtool' installed."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`automake' installed."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(aclocal --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: Missing \`aclocal'.  The version of \`automake'"
+  echo "installed doesn't appear recent enough."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+(pkg-config --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`pkg-config' installed."
+  DIE=1
+}
+
+if test "$DIE" -eq 1; then
+  exit 1
+fi
+
 set -x
 
 rm -rf autom4te*.cache
index 4763046063a2ce5cd491e74dd8f0c46a831fa96c..e72c38f39f3044065f772f7e4c79e7562b1b0cf4 100644 (file)
@@ -6,13 +6,19 @@ SUFFIXES = .tmpl .
 TMPLFILES = afp.conf.tmpl
 GENFILES = afp.conf
 CLEANFILES = $(GENFILES)
-EXTRA_DIST = afp.conf.tmpl
+EXTRA_DIST = afp.conf.tmpl extmap.conf netatalk-dbus.conf
 
 OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
 
-CONFFILES = 
+CONFFILES = extmap.conf
 
 pkgconfdir = @PKGCONFDIR@
+
+if HAVE_DBUS_GLIB
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = netatalk-dbus.conf
+endif
+
 #
 # rule to parse template files
 #
diff --git a/config/extmap.conf b/config/extmap.conf
new file mode 100644 (file)
index 0000000..44ccec5
--- /dev/null
@@ -0,0 +1,308 @@
+# Netatalk file extension -> OS X type/creator mapping configuration file
+#
+# Delete a '#' character at the head of line to uncomment and enable a mapping
+#
+### Default translation:
+#.         "????"  "????"      Unix Binary                    Unix                      application/octet-stream
+#.         "BINA"  "UNIX"      Unix Binary                    Unix                      application/octet-stream
+#.         "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+
+#.1st      "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+#.669      "6669"  "SNPL"      669 MOD Music                  PlayerPro
+#.8med     "STrk"  "SCPL"      Amiga OctaMed music            SoundApp
+#.8svx     "8SVX"  "SCPL"      Amiga 8-bit sound              SoundApp
+#.a        "TEXT"  "ttxt"      Assembly Source                SimpleText
+#.aif      "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+#.aifc     "AIFC"  "SCPL"      AIFF Sound Compressed          SoundApp                  audio/x-aiff
+#.aiff     "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+#.al       "ALAW"  "SCPL"      ALAW Sound                     SoundApp
+#.ani      "ANIi"  "GKON"      Animated NeoChrome             GraphicConverter
+#.apd      "TEXT"  "ALD3"      Aldus Printer Description      Aldus PageMaker
+#.arc      "mArc"  "SITx"      PC ARChive                     StuffIt Expander
+#.arj      "BINA"  "DArj"      ARJ Archive                    DeArj
+#.arr      "ARR "  "GKON"      Amber ARR image                GraphicConverter
+#.art      "ART "  "GKON"      First Publisher                GraphicConverter
+#.asc      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.ascii    "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.asf      "ASF_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+#.asm      "TEXT"  "ttxt"      Assembly Source                SimpleText
+#.asx      "ASX_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+#.au       "ULAW"  "TVOD"      Sun Sound                      QuickTime Player          audio/basic
+#.avi      "VfW "  "TVOD"      AVI Movie                      QuickTime Player          video/avi
+#.bar      "BARF"  "S691"      Unix BAR Archive               SunTar
+#.bas      "TEXT"  "ttxt"      BASIC Source                   SimpleText
+#.bat      "TEXT"  "ttxt"      MS-DOS Batch File              SimpleText
+#.bga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+#.bib      "TEXT"  "ttxt"      BibTex Bibliography            SimpleText
+#.bin      "SIT!"  "SITx"      MacBinary                      StuffIt Expander          application/macbinary
+#.binary   "BINA"  "hDmp"      Untyped Binary Data            HexEdit                   application/octet-stream
+#.bmp      "BMPp"  "ogle"      Windows Bitmap                 PictureViewer
+#.boo      "TEXT"  "ttxt"      BOO encoded                    SimpleText
+#.bst      "TEXT"  "ttxt"      BibTex Style                   SimpleText
+#.bw       "SGI "  "GKON"      SGI Image                      GraphicConverter
+#.c        "TEXT"  "CWIE"      C Source                       CodeWarrior
+#.cgm      "CGMm"  "GKON"      Computer Graphics Meta         GraphicConverter
+#.class    "Clss"  "CWIE"      Java Class File                CodeWarrior
+#.clp      "CLPp"  "GKON"      Windows Clipboard              GraphicConverter
+#.cmd      "TEXT"  "ttxt"      OS/2 Batch File                SimpleText
+#.com      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+#.cp       "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+#.cpp      "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+#.cpt      "PACT"  "SITx"      Compact Pro Archive            StuffIt Expander
+#.csv      "TEXT"  "XCEL"      Comma Separated Vars           Excel
+#.ct       "..CT"  "GKON"      Scitex-CT                      GraphicConverter
+#.cut      "Halo"  "GKON"      Dr Halo Image                  GraphicConverter
+#.cvs      "drw2"  "DAD2"      Canvas Drawing                 Canvas
+#.dbf      "COMP"  "FOX+"      DBase Document                 FoxBase+
+#.dcx      "DCXx"  "GKON"      Some PCX Images                GraphicConverter
+#.dif      "TEXT"  "XCEL"      Data Interchange Format        Excel
+#.diz      "TEXT"  "R*Ch"      BBS Descriptive Text           BBEdit
+#.dl       "DL  "  "AnVw"      DL Animation                   MacAnim Viewer
+#.dll      "PCFL"  "SWIN"      Windows DLL                    SoftWindows
+#.doc      "WDBN"  "MSWD"      Word Document                  Microsoft Word            application/msword
+#.dot      "sDBN"  "MSWD"      Word for Windows Template      Microsoft Word
+#.dvi      "ODVI"  "xdvi"      TeX DVI Document               xdvi                      application/x-dvi
+#.dwt      "TEXT"  "DmWr"      Dreamweaver Template           Dreamweaver
+#.dxf      "TEXT"  "SWVL"      AutoCAD 3D Data                Swivel Pro
+#.eps      "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+#.epsf     "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+#.etx      "TEXT"  "ezVu"      SEText                         Easy View                 text/x-setext
+#.evy      "EVYD"  "ENVY"      Envoy Document                 Envoy
+#.exe      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+#.faq      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/x-usenet-faq
+#.fit      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter          image/x-fits
+#.fla      "SPA "  "MFL2"      Flash source                   Macromedia Flash
+#.flc      "FLI "  "TVOD"      FLIC Animation                 QuickTime Player
+#.fli      "FLI "  "TVOD"      FLI Animation                  QuickTime Player
+#.fm       "FMPR"  "FMPR"      FileMaker Pro Database         FileMaker Pro
+#.for      "TEXT"  "MPS "      Fortran Source                 MPW Shell
+#.fts      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter
+#.gem      "GEM-"  "GKON"      GEM Metafile                   GraphicConverter
+#.gif      "GIFf"  "ogle"      GIF Picture                    PictureViewer             image/gif
+#.gl       "GL  "  "AnVw"      GL Animation                   MacAnim Viewer
+#.grp      "GRPp"  "GKON"      GRP Image                      GraphicConverter
+#.gz       "SIT!"  "SITx"      Gnu ZIP Archive                StuffIt Expander          application/x-gzip
+#.h        "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hcom     "FSSD"  "SCPL"      SoundEdit Sound ex SOX         SoundApp
+#.hp       "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hpgl     "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+#.hpp      "TEXT"  "CWIE"      C Include File                 CodeWarrior
+#.hqx      "TEXT"  "SITx"      BinHex                         StuffIt Expander          application/mac-binhex40
+#.htm      "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+#.html     "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+#.i3       "TEXT"  "R*ch"      Modula 3 Interface             BBEdit
+#.ic1      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.ic2      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.ic3      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+#.icn      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+#.ico      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+#.ief      "IEF "  "GKON"      IEF image                      GraphicConverter          image/ief
+#.iff      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+#.ilbm     "ILBM"  "GKON"      Amiga ILBM Image               GraphicConverter
+#.image    "dImg"  "ddsk"      Apple DiskCopy Image           Disk Copy
+#.img      "IMGg"  "GKON"      GEM bit image/XIMG             GraphicConverter
+#.ini      "TEXT"  "ttxt"      Windows INI File               SimpleText
+#.java     "TEXT"  "CWIE"      Java Source File               CodeWarrior
+#.jfif     "JPEG"  "ogle"      JFIF Image                     PictureViewer
+#.jpe      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.jpeg     "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.jpg      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+#.latex    "TEXT"  "OTEX"      Latex                          OzTex                     application/x-latex
+#.lbm      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+#.lha      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+#.lzh      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+#.m1a      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.m1s      "MPEG"  "TVOD"      MPEG-1 systemstream            MoviePlayer
+#.m1v      "M1V "  "TVOD"      MPEG-1 IPB videostream         MoviePlayer               video/mpeg
+#.m2       "TEXT"  "R*ch"      Modula 2 Source                BBEdit
+#.m2v      "MPG2"  "MPG2"      MPEG-2 IPB videostream         MPEG2decoder
+#.m3       "TEXT"  "R*ch"      Modula 3 Source                BBEdit
+#.mac      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.mak      "TEXT"  "R*ch"      Makefile                       BBEdit
+#.mcw      "WDBN"  "MSWD"      Mac Word Document              Microsoft Word
+#.me       "TEXT"  "ttxt"      Text Readme                    SimpleText
+#.med      "STrk"  "SCPL"      Amiga MED Sound                SoundApp
+#.mf       "TEXT"  "*MF*"      Metafont                       Metafont
+#.mid      "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+#.midi     "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+#.mif      "TEXT"  "Fram"      FrameMaker MIF                 FrameMaker                application/x-framemaker
+#.mime     "TEXT"  "SITx"      MIME Message                   StuffIt Expander          message/rfc822
+#.ml       "TEXT"  "R*ch"      ML Source                      BBEdit
+#.mod      "STrk"  "SCPL"      MOD Music                      SoundApp
+#.mol      "TEXT"  "RSML"      MDL Molfile                    RasMac
+#.moov     "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.mov      "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.mp2      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.mp3      "MPG3"  "TVOD"      MPEG-3 audiostream             MoviePlayer               audio/x-mpeg
+#.mpa      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+#.mpe      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.mpeg     "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.mpg      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+#.msp      "MSPp"  "GKON"      Microsoft Paint                GraphicConverter
+#.mtm      "MTM "  "SNPL"      MultiMOD Music                 PlayerPro
+#.mw       "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+#.mwii     "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+#.neo      "NeoC"  "GKON"      Atari NeoChrome                GraphicConverter
+#.nfo      "TEXT"  "ttxt"      Info Text                      SimpleText                application/text
+#.nst      "STrk"  "SCPL"      MOD Music                      SoundApp
+#.obj      "PCFL"  "SWIN"      Object (DOS/Windows)           SoftWindows
+#.oda      "ODIF"  "ODA "      ODA Document                   MacODA XTND Translator    application/oda
+#.okt      "OKTA"  "SCPL"      Oktalyser MOD Music            SoundApp
+#.out      "BINA"  "hDmp"      Output File                    HexEdit
+#.ovl      "PCFL"  "SWIN"      Overlay (DOS/Windows)          SoftWindows
+#.p        "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+#.pac      "STAD"  "GKON"      Atari STAD Image               GraphicConverter
+#.pas      "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+#.pbm      "PPGM"  "GKON"      Portable Bitmap                GraphicConverter          image/x-portable-bitmap
+#.pc1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pc2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pc3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pcs      "PICS"  "GKON"      Animated PICTs                 GraphicConverter
+#.pct      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.pcx      "PCXx"  "GKON"      PC PaintBrush                  GraphicConverter
+#.pdb      "TEXT"  "RSML"      Brookhaven PDB file            RasMac
+#.pdf      "PDF "  "CARO"      Portable Document Format       Acrobat Reader            application/pdf
+#.pdx      "TEXT"  "ALD5"      Printer Description            PageMaker
+#.pf       "CSIT"  "SITx"      Private File                   StuffIt Expander 
+#.pgm      "PPGM"  "GKON"      Portable Graymap               GraphicConverter          image/x-portable-graymap
+#.pi1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pi2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pi3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+#.pic      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+#.pict     "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-macpict
+#.pit      "PIT "  "SITx"      PackIt Archive                 StuffIt Expander
+#.pkg      "HBSF"  "SITx"      AppleLink Package              StuffIt Expander
+#.pl       "TEXT"  "McPL"      Perl Source                    MacPerl
+#.plt      "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+#.pm       "PMpm"  "GKON"      Bitmap from xv                 GraphicConverter
+#.pm3      "ALB3"  "ALD3"      PageMaker 3 Document           PageMaker
+#.pm4      "ALB4"  "ALD4"      PageMaker 4 Document           PageMaker
+#.pm5      "ALB5"  "ALD5"      PageMaker 5 Document           PageMaker
+#.png      "PNG "  "ogle"      Portable Network Graphic       PictureViewer
+#.pntg     "PNTG"  "ogle"      Macintosh Painting             PictureViewer
+#.ppd      "TEXT"  "ALD5"      Printer Description            PageMaker
+#.ppm      "PPGM"  "GKON"      Portable Pixmap                GraphicConverter          image/x-portable-pixmap
+#.prn      "TEXT"  "R*ch"      Printer Output File            BBEdit
+#.ps       "TEXT"  "vgrd"      PostScript                     LaserWriter 8             application/postscript
+#.psd      "8BPS"  "8BIM"      PhotoShop Document             Photoshop
+#.pt4      "ALT4"  "ALD4"      PageMaker 4 Template           PageMaker
+#.pt5      "ALT5"  "ALD5"      PageMaker 5 Template           PageMaker
+#.pxr      "PXR "  "8BIM"      Pixar Image                    Photoshop
+#.qdv      "QDVf"  "GKON"      QDV image                      GraphicConverter
+#.qt       "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+#.qxd      "XDOC"  "XPR3"      QuarkXpress Document           QuarkXpress
+#.qxt      "XTMP"  "XPR3"      QuarkXpress Template           QuarkXpress
+#.raw      "BINA"  "GKON"      Raw Image                      GraphicConverter
+#.readme   "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+#.rgb      "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+#.rgba     "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+#.rib      "TEXT"  "RINI"      Renderman 3D Data              Renderman
+#.rif      "RIFF"  "GKON"      RIFF Graphic                   GraphicConverter
+#.rle      "RLE "  "GKON"      RLE image                      GraphicConverter
+#.rme      "TEXT"  "ttxt"      Text Readme                    SimpleText
+#.rpl      "FRL!"  "REP!"      Replica Document               Replica
+#.rsc      "rsrc"  "RSED"      Resource File                  ResEdit
+#.rsrc     "rsrc"  "RSED"      Resource File                  ResEdit
+#.rtf      "TEXT"  "MSWD"      Rich Text Format               Microsoft Word            application/rtf
+#.rtx      "TEXT"  "R*ch"      Rich Text                      BBEdit                    text/richtext
+#.s3m      "S3M "  "SNPL"      ScreamTracker 3 MOD            PlayerPro
+#.scc      "MSX "  "GKON"      MSX pitcure                    GraphicConverter
+#.scg      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.sci      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scp      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scr      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.scu      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+#.sea      "APPL"  "????"      Self-Extracting Archive        Self Extracting Archive
+#.sf       "IRCM"  "SDHK"      IRCAM Sound                    SoundHack
+#.sgi      ".SGI"  "ogle"      SGI Image                      PictureViewer
+#.sha      "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+#.shar     "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+#.shp      "SHPp"  "GKON"      Printmaster Icon Library       GraphicConverter
+#.sit      "SIT!"  "SITx"      StuffIt 1.5.1 Archive          StuffIt Expander          application/x-stuffit
+#.sithqx   "TEXT"  "SITx"      BinHexed StuffIt Archive       StuffIt Expander          application/mac-binhex40
+#.six      "SIXE"  "GKON"      SIXEL image                    GraphicConverter
+#.slk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.snd      "BINA"  "SCPL"      Sound of various types         SoundApp
+#.spc      "Spec"  "GKON"      Atari Spectrum 512             GraphicConverter
+#.sr       "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+#.sty      "TEXT"  "*TEX"      TeX Style                      Textures
+#.sun      "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+#.sup      "SCRN"  "GKON"      StartupScreen                  GraphicConverter
+#.svx      "8SVX"  "SCPL"      Amiga IFF Sound                SoundApp
+#.syk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.sylk     "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+#.tar      "TARF"  "SITx"      Unix Tape ARchive              StuffIt Expander          application/x-tar
+#.targa    "TPIC"  "GKON"      Truevision Image               GraphicConverter
+#.taz      "ZIVU"  "SITx"      Compressed Tape ARchive        StuffIt Expander          application/x-compress
+#.tex      "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-tex
+#.texi     "TEXT"  "OTEX"      TeX Document                   OzTeX
+#.texinfo  "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-texinfo
+#.text     "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.tga      "TPIC"  "GKON"      Truevision Image               GraphicConverter
+#.tgz      "Gzip"  "SITx"      Gnu ZIPed Tape ARchive         StuffIt Expander          application/x-gzip
+#.tif      "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+#.tiff     "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+#.tny      "TINY"  "GKON"      Atari TINY Bitmap              GraphicConverter
+#.tsv      "TEXT"  "XCEL"      Tab Separated Values           Excel                     text/tab-separated-values
+#.tx8      "TEXT"  "ttxt"      8-bit ASCII Text               SimpleText
+#.txt      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+#.ul       "ULAW"  "TVOD"      Mu-Law Sound                   MoviePlayer               audio/basic
+#.url      "AURL"  "Arch"      URL Bookmark                   Anarchie                  message/external-body
+#.uu       "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+#.uue      "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+#.vff      "VFFf"  "GKON"      DESR VFF Greyscale Image       GraphicConverter
+#.vga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+#.voc      "VOC "  "SCPL"      VOC Sound                      SoundApp
+#.w51      ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+#.wav      "WAVE"  "TVOD"      Windows WAV Sound              MoviePlayer               audio/x-wav
+#.wk1      "XLBN"  "XCEL"      Lotus Spreadsheet r2.1         Excel
+#.wks      "XLBN"  "XCEL"      Lotus Spreadsheet r1.x         Excel
+#.wmf      "WMF "  "GKON"      Windows Metafile               GraphicConverter
+#.wp       ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+#.wp4      ".WP4"  "WPC2"      WordPerfect PC 4.2 Doc         WordPerfect
+#.wp5      ".WP5"  "WPC2"      WordPerfect PC 5.x Doc         WordPerfect               application/wordperfect5.1
+#.wp6      ".WP6"  "WPC2"      WordPerfect PC 6.x Doc         WordPerfect
+#.wpg      "WPGf"  "GKON"      WordPerfect Graphic            GraphicConverter
+#.wpm      "WPD1"  "WPC2"      WordPerfect Mac                WordPerfect
+#.wri      "WDBN"  "MSWD"      MS Write/Windows               Microsoft Word
+#.wve      "BINA"  "SCPL"      PSION sound                    SoundApp
+#.x10      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.x11      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.xbm      "XBM "  "GKON"      X-Windows Bitmap               GraphicConverter          image/x-xbm
+#.xl       "XLS "  "XCEL"      Excel Spreadsheet              Excel
+#.xlc      "XLC "  "XCEL"      Excel Chart                    Excel
+#.xlm      "XLM "  "XCEL"      Excel Macro                    Excel
+#.xls      "XLS "  "XCEL"      Excel Spreadsheet              Excel
+#.xlw      "XLW "  "XCEL"      Excel Workspace                Excel
+#.xm       "XM  "  "SNPL"      FastTracker MOD Music          PlayerPro
+#.xpm      "XPM "  "GKON"      X-Windows Pixmap               GraphicConverter          image/x-xpm
+#.xwd      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+#.Z        "ZIVU"  "SITx"      Unix Compress Archive          StuffIt Expander          application/x-compress
+#.zip      "ZIP "  "SITx"      PC ZIP Archive                 StuffIt Expander          application/zip
+#.zoo      "Zoo "  "Booz"      Zoo Archive                    MacBooz
+
+# I'd like to dedicate this as follows code to Miss.Tamaki Imazu
+#
+# Kazuhiko Okudaira the Nursery Teacher
+# kokudaira@hotmail.com
+
+#.bld  "BLD "  "GKON"  BLD                            GraphicConverter
+#.bum  ".bMp"  "GKON"  QuickTime Importer(QuickDraw)  GraphicConverter
+#.cel  "CEL "  "GKON"  KISS CEL                       GraphicConverter
+#.cur  "CUR "  "GKON"  Windows Cursor                 GraphicConverter
+#.cwj  "CWSS"  "cwkj"  ClarisWorks Document           ClarisWorks 4.0
+#.dat  "TCLl"  "GKON"  TCL image                      GraphicConverter
+#.hr   "TR80"  "GKON"  TSR-80 HR                      GraphicConverter
+#.iss  "ISS "  "GKON"  ISS                            GraphicConverter
+#.jif  "JIFf"  "GKON"  JIF99a                         GraphicConverter
+#.lwf  "lwfF"  "GKON"  LuraWave(LWF)                  GraphicConverter
+#.mbm  "MBM "  "GKON"  PSION 5(MBM)                   GraphicConverter
+#.ngg  "NGGC"  "GKON"  Mobile Phone(Nokia)Format      GraphicConverter
+#.nol  "NOL "  "GKON"  Mobile Phone(Nokia)Format      GraphicConverter
+#.pal  "8BCT"  "8BIM"  Color Table                    GraphicConverter
+#.pgc  "PGCF"  "GKON"  PGC/PGF  Atari Portfolio PCG   GraphicConverter
+#.pics "PICS"  "GKON"  PICS-PICT Sequence             GraphicConverter
+#.swf  "SWFL"  "SWF2"  Flash                          Macromedia Flash
+#.vpb  "VPB "  "GKON"  VPB QUANTEL                    GraphicConverter
+#.wbmp "WBMP"  "GKON"  WBMP                           GraphicConverter
+#.x-face  "TEXT"  "GKON"  X-Face                      GraphicConverter
diff --git a/config/netatalk-dbus.conf b/config/netatalk-dbus.conf
new file mode 100644 (file)
index 0000000..1646efd
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE busconfig PUBLIC
+          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- Only root can own AFP stats service -->
+  <policy user="root">
+    <allow own="org.netatalk.AFPStats"/>
+  </policy>
+
+  <!-- Allow anyone to invoke methods on time-sliderd -->
+  <policy context="default">
+    <allow send_destination="org.netatalk.AFPStats"/>
+    <allow receive_sender="org.netatalk.AFPStats"/>
+  </policy>
+
+</busconfig>
index dbbab4b2b68881f9239845ac8595eec98f1272d7..d209ccf96dcf4b7da7c1d8ae5208136bbe3569a7 100644 (file)
@@ -1,5 +1,5 @@
 Makefile
 Makefile.in
 netatalk.pam
-.gitignore
+netatalk
 *.o
index e01d784c675d54261bdba14f05a5d0321d21c7ad..62affbdac21e5b18603b6cb3879352a3ebab74aa 100644 (file)
@@ -1,10 +1,9 @@
 ## Makefile for distrib/pam/
 
 SUFFIXES = .tmpl .
-pamdir = @PAMDIR@/etc/pam.d
-EXTRA_DIST = netatalk.pam.tmpl
-noinst_SCRIPTS = netatalk.pam
-CLEANFILES = netatalk.pam
+EXTRA_DIST = netatalk.tmpl
+noinst_SCRIPTS = netatalk
+CLEANFILES = netatalk
 
 .tmpl:
        sed -e "s,[@]PAM_DIRECTIVE[@],${PAM_DIRECTIVE},g" \
@@ -15,23 +14,6 @@ CLEANFILES = netatalk.pam
            <$< >$@
 
 if USE_PAM
-install-data-local: netatalk.pam
-       $(mkinstalldirs) $(DESTDIR)$(pamdir)
-       if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pamdir)/netatalk; then \
-           echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pamdir)/netatalk"; \
-           $(INSTALL_DATA) netatalk.pam $(DESTDIR)$(pamdir)/netatalk || echo "WARNING: Can't install PAM files";               \
-       else \
-           echo "not overwriting $(DESTDIR)$(pamdir)/netatalk"; \
-       fi; 
-
-uninstall-local:
-       echo rm -f $(DESTDIR)$(pamdir)/netatalk; \
-       rm -f $(DESTDIR)$(pamdir)/netatalk; \
-       for f in $(CONFFILES) $(GENFILES); do \
-               echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
-               rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
-       done
-else
-install-data-local:
-uninstall-local:
+pamdir = $(PAMDIR)
+pam_DATA = netatalk
 endif
diff --git a/config/pam/netatalk.pam.tmpl b/config/pam/netatalk.pam.tmpl
deleted file mode 100644 (file)
index 1eceba3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#%PAM-1.0
-auth     @PAM_DIRECTIVE@ @PAM_AUTH@
-account  @PAM_DIRECTIVE@ @PAM_ACCOUNT@
-password @PAM_DIRECTIVE@ @PAM_PASSWORD@
-session  @PAM_DIRECTIVE@ @PAM_SESSION@
diff --git a/config/pam/netatalk.tmpl b/config/pam/netatalk.tmpl
new file mode 100644 (file)
index 0000000..1eceba3
--- /dev/null
@@ -0,0 +1,5 @@
+#%PAM-1.0
+auth     @PAM_DIRECTIVE@ @PAM_AUTH@
+account  @PAM_DIRECTIVE@ @PAM_ACCOUNT@
+password @PAM_DIRECTIVE@ @PAM_PASSWORD@
+session  @PAM_DIRECTIVE@ @PAM_SESSION@
index 49f70c603f2f4946763177f11df9aa772d559778..e4d910c33fdb4e998475e5ff1e3251e91fafe267 100644 (file)
@@ -74,17 +74,15 @@ AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
 
 dnl these tests have been comfirmed to be needed in 2011
 AC_CHECK_FUNCS(backtrace_symbols dirfd getusershell pread pwrite pselect)
-AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen)
+AC_CHECK_FUNCS(setlinebuf strlcat strlcpy strnlen mempcpy)
 AC_CHECK_FUNCS(mmap utime getpagesize) dnl needed by tbd
 
 dnl search for necessary libraries
 AC_SEARCH_LIBS(gethostbyname, nsl)
 AC_SEARCH_LIBS(connect, socket)
-AC_SEARCH_LIBS(pthread_sigmask, pthread,,[AC_MSG_ERROR([missing pthread_sigmask])])
-if test x"$ac_cv_search_pthread_sigmask" != x"none required" ; then
-   PTHREAD_LIBS=$ac_cv_search_pthread_sigmask
-fi
-AC_SUBST(PTHREAD_LIBS)
+AC_CHECK_FUNCS(getifaddrs) dnl comes after gethostbyname and connect so it picks up the libs
+
+AX_PTHREAD(, [AC_MSG_ERROR([missing pthread_sigmask])])
 
 AC_DEFINE(OPEN_NOFOLLOW_ERRNO, ELOOP, errno returned by open with O_NOFOLLOW)
 
@@ -184,12 +182,25 @@ AC_NETATALK_SENDFILE
 dnl Check whether bundled libevent shall not be used
 AC_NETATALK_LIBEVENT
 
+dnl libatalk API checks
+AC_DEVELOPER
+
+dnl Check for dtrace
+AC_NETATALK_DTRACE
+
+dnl Check for dbus-glib, for AFP stats on dbus
+AC_NETATALK_DBUS_GLIB
+
 dnl FHS stuff has to be done last because it overrides other defaults
 AC_NETATALK_FHS
 
-AC_DEVELOPER
+dnl netatalk lockfile path, must come after AC_NETATALK_FHS
+AC_NETATALK_LOCKFILE
+
+dnl Check for Docbook and build documentation if found
+AX_CHECK_DOCBOOK
 
-CFLAGS="-I\$(top_srcdir)/include -I\$(top_srcdir)/sys $CFLAGS"
+CFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/include $CFLAGS"
 UAMS_PATH="${uams_path}"
 
 AC_SUBST(LIBS)
@@ -240,6 +251,14 @@ AC_OUTPUT([Makefile
        distrib/initscripts/Makefile
        distrib/m4/Makefile
        doc/Makefile
+    doc/html.xsl
+    doc/man.xsl
+    doc/manual/Makefile
+    doc/manual/manual.xml
+    doc/manpages/Makefile
+    doc/manpages/man1/Makefile
+    doc/manpages/man5/Makefile
+    doc/manpages/man8/Makefile
        etc/Makefile
        etc/afpd/Makefile
        etc/cnid_dbd/Makefile
@@ -267,8 +286,26 @@ AC_OUTPUT([Makefile
        macros/Makefile
        man/Makefile
        man/man1/Makefile
+    man/man1/ad.1
+    man/man1/afpldaptest.1
+    man/man1/afppasswd.1
+    man/man1/afpstats.1
+    man/man1/apple_dump.1
+    man/man1/asip-status.pl.1
+    man/man1/dbd.1
+    man/man1/macusers.1
+    man/man1/netatalk-config.1
+    man/man1/uniconv.1
        man/man5/Makefile
+    man/man5/afp.conf.5
+    man/man5/afp_signature.conf.5
+    man/man5/afp_voluuid.conf.5
+    man/man5/extmap.conf.5
        man/man8/Makefile
+    man/man8/afpd.8
+    man/man8/cnid_dbd.8
+    man/man8/cnid_metad.8
+    man/man8/netatalk.8
        test/Makefile
        test/afpd/Makefile
        ],
index 7d69713e3249932c9282f1adfefb490d6a28d3d6..14245d2d6cc987819bb2fe081b8fac731c387dd1 100644 (file)
@@ -1,8 +1,35 @@
 diff --git a/libevent/Makefile.am b/libevent/Makefile.am
-index 46f6d34..dda19b2 100644
+index 09505bd..8114ad3 100644
 --- a/libevent/Makefile.am
 +++ b/libevent/Makefile.am
-@@ -173,7 +173,7 @@ NO_UNDEFINED =
+@@ -86,9 +86,6 @@ if INSTALL_LIBEVENT
+ dist_bin_SCRIPTS = event_rpcgen.py
+ endif
+-pkgconfigdir=$(libdir)/pkgconfig
+-LIBEVENT_PKGCONFIG=libevent.pc
+-
+ # These sources are conditionally added by configure.in or conditionally
+ # included from other files.
+ PLATFORM_DEPENDENT_SRC = \
+@@ -108,16 +105,13 @@ EXTRA_DIST = \
+ LIBEVENT_LIBS_LA = libevent.la libevent_core.la libevent_extra.la
+ if PTHREADS
+ LIBEVENT_LIBS_LA += libevent_pthreads.la
+-LIBEVENT_PKGCONFIG += libevent_pthreads.pc
+ endif
+ if OPENSSL
+ LIBEVENT_LIBS_LA += libevent_openssl.la
+-LIBEVENT_PKGCONFIG += libevent_openssl.pc
+ endif
+ if INSTALL_LIBEVENT
+ lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA)
+-pkgconfig_DATA = $(LIBEVENT_PKGCONFIG)
+ else
+ noinst_LTLIBRARIES =  $(LIBEVENT_LIBS_LA)
+ endif
+@@ -196,7 +190,7 @@ NO_UNDEFINED =
  MAYBE_CORE =
  endif
  
@@ -11,9 +38,12 @@ index 46f6d34..dda19b2 100644
  
  libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
  libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
-@@ -221,3 +221,5 @@ FORCE:
- DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
+@@ -249,5 +243,7 @@ doxygen: FORCE
+       doxygen $(srcdir)/Doxyfile
+ FORCE:
  
-+install:
+-DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
++DISTCLEANFILES = *~ ./include/event2/event-config.h
 +
++install:
index bb4d9cbf0637381a924e0a0d2f41b8364381ce72..dc4c7db7076b6f2b1de73ed7546a2acb1c3a182c 100644 (file)
@@ -18,6 +18,6 @@ SUFFIXES = .tmpl .
 
 CLEANFILES = $(GENERATED_FILES)
 
-bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
+bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES) afpstats
 
-EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl
+EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl afpstats
diff --git a/contrib/shell_utils/afpstats b/contrib/shell_utils/afpstats
new file mode 100755 (executable)
index 0000000..8c5413c
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python afpstats.py
+"""
+
+import sys
+from traceback import print_exc
+import dbus
+
+def main():
+    bus = dbus.SystemBus()
+
+    try:
+        remote_object = bus.get_object("org.netatalk.AFPStats",
+                                       "/org/netatalk/AFPStats")
+
+    except dbus.DBusException:
+        print_exc()
+        sys.exit(1)
+
+    iface = dbus.Interface(remote_object, "org.netatalk.AFPStats")
+
+    reply = iface.GetUsers()
+    for name in reply:
+        print name
+
+if __name__ == '__main__':
+    main()
index 6054e0f8bb26bb5a71382a9f3736619e981ba0f1..30dbb425ba957317765dbfb3d152b862e184be22 100755 (executable)
@@ -50,6 +50,7 @@ use File::Basename;
 use File::Spec;
 use File::Temp qw /tempfile/;
 use bigint; # require perl >= 5.8
+use IPC::Open2 qw /open2/;
 
 # check command for extended attributes -----------------------------------
 
@@ -841,50 +842,46 @@ sub hexdump {
 sub checkea {
     my ($file) = @_;
 
-    $file =~ s/\\/\\\\/g;
-    $file =~ s/\"/\\\"/g;
-    $file =~ s/\$/\\\$/g;
-    $file =~ s/\`/\\\`/g;
     if ( $eacommand == 1 ) {
-        open(EALIST, "getfattr \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'getfattr', $file) or die $@;
         while(<EALIST>) {
             if ( $_ eq "user.org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 2 ) {
-        open(EALIST, "attr -q -l \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'attr', '-q', '-l', $file) or die $@;
         while(<EALIST>) {
             if ( $_ eq "org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 3 ) {
-        open(EALIST, "runat \"$file\" ls -1 |");
+        open2(\*EALIST, \*EAIN, 'runat', $file, 'ls', '-1') or die $@;
         while(<EALIST>) {
             if ( $_ eq "org.netatalk.Metadata\n" ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } elsif ( $eacommand == 4 ) {
-        open(EALIST, "lsextattr -q user \"$file\" |");
+        open2(\*EALIST, \*EAIN, 'lsextattr', '-q', 'user', $file) or die $@;
         while(<EALIST>) {
             $_ = "\t".$_;
             if ( $_ =~ /\torg\.netatalk\.Metadata[\n\t]/ ) {
-                close (EALIST);
+                close (EALIST, EAIN);
                 return 1;
             }
         }
-        close (EALIST);
+        close (EALIST, EAIN);
         return 0;
     } else {
         return 0;
@@ -893,25 +890,24 @@ sub checkea {
 
 sub eaopenfile {
     my ($file) = @_;
-
-    $file =~ s/\\/\\\\/g;
-    $file =~ s/\"/\\\"/g;
-    $file =~ s/\$/\\\$/g;
-    $file =~ s/\`/\\\`/g;
-    ($eatempfh, $eatempfile) = tempfile(UNLINK => 1);
+    my @eacommands = ();
 
     if ( $eacommand == 1 ) {
-        system("getfattr --only-values -n user.org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('getfattr', '--only-values', '-n', 'user.org.netatalk.Metadata', $file);
     } elsif ( $eacommand == 2 ) {
-        system("attr -q -g org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('attr', '-q', '-g', 'org.netatalk.Metadata', $file);
     } elsif ( $eacommand == 3 ) {
-        system("runat \"$file\" cat org.netatalk.Metadata > $eatempfile");
+        @eacommands = ('runat', $file, 'cat', 'org.netatalk.Metadata',);
     } elsif ( $eacommand == 4 ) {
-        system("getextattr -q user org.netatalk.Metadata \"$file\" > $eatempfile");
+        @eacommands = ('getextattr', '-q', 'user', 'org.netatalk.Metadata', $file);
     } else {
         return "";
     }
 
+    my ($eatempfh, $eatempfile) = tempfile(UNLINK => 1);
+    open2(my $ealist, my $eain, @eacommands) or die $@;
+    print $eatempfh $_ while(<$ealist>);
+    close($ealist, $eain);
     close($eatempfh);
     return $eatempfile;
 }
index 4b88ba9d39f6ac37bc4195ff89f0b9459fcb951a..79dcfb3f283179abfcfb342ff095881577113c18 100644 (file)
@@ -13,6 +13,7 @@ pkgconfdir = @PKGCONFDIR@
            -e s@:SBINDIR:@${sbindir}@ \
            -e s@:ETCDIR:@${pkgconfdir}@ \
            -e s@:NETATALK_VERSION:@${NETATALK_VERSION}@ \
+           -e s@:PATH_NETATALK_LOCK:@${PATH_NETATALK_LOCK}@ \
            <$< >$@
 
 GENERATED_FILES = \
@@ -39,6 +40,7 @@ TEMPLATES = \
 
 CLEANFILES = $(GENERATED_FILES) $(sysv_SCRIPTS) $(service_DATA) afpd cnid_metad
 EXTRA_DIST = $(TEMPLATES)
+noinst_DATA = $(GENERATED_FILES)
 
 # overwrite automake uninstall
 # not beautiful, but this way we can call the OS specific init script
@@ -52,7 +54,7 @@ uninstall: uninstall-startup
 
 if USE_REDHAT_SYSV
 
-sysvdir        = /etc/rc.d/init.d
+sysvdir        = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.redhat
@@ -74,7 +76,7 @@ endif
 
 if USE_SYSTEMD
 
-servicedir     = /lib/systemd/system
+servicedir     = $(INIT_DIR)
 service_DATA   = netatalk.service
 
 netatalk.service: service.systemd
@@ -96,7 +98,7 @@ endif
 
 if USE_SUSE_SYSV
 
-sysvdir        = /etc/init.d
+sysvdir        = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.suse
@@ -118,7 +120,7 @@ endif
 
 if USE_NETBSD
 
-sysvdir = /etc/rc.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 netatalk: rc.netbsd
@@ -139,7 +141,7 @@ endif
 
 if USE_SOLARIS
 
-servicedir = /lib/svc/manifest/network/
+servicedir = $(INIT_DIR)
 service_DATA = netatalk.xml
 
 install-data-hook:
@@ -156,7 +158,7 @@ endif
 
 if USE_GENTOO
 
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.gentoo
@@ -178,7 +180,7 @@ endif
 
 if USE_DEBIAN
 
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
 sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.debian
index 643448ae688869d8cb44b0b1f6fb59e708fc5881..0721d9f64a9c2c9e8e113c53c6633066c08f1cab 100644 (file)
@@ -21,7 +21,7 @@ RETVAL=1
 netatalk_startup() {
     # Check that networking is up.
     if [ ${NETWORKING} = "no" ]; then
-        echo "[Network isn't started]"; 
+        echo "[Network isn't started]";
         exit 1;
     fi
 
@@ -51,11 +51,17 @@ netatalk_stop() {
     fi
 }
 
-# code to cause apfd and cnid_metad to restart
-netatalk_graceful() {
+# restart code
+netatalk_restart() {
+    netatalk_stop
+    netatalk_startup
+}
+
+# reload config files
+netatalk_reload() {
     if [ -x ${NETATALK_SBIN}/netatalk ]; then
-        echo -n $"Restarting cnid_metad and afpd: "
-        killproc  netatalk -QUIT
+        echo -n $"Reloading $prog: "
+        killproc netatalk -HUP
         RETVAL=$?
         echo
     fi
@@ -68,20 +74,21 @@ case "$1" in
     'stop')
         netatalk_stop
         ;;
-    'restart'|'reload')
-        $0 stop
-        $0 start
-        RETVAL=$?
+    'restart')
+        netatalk_restart
+        ;;
+    'reload'|'graceful')
+        netatalk_reload
         ;;
     'status')
         status netatalk
         RETVAL=$?
         ;;
-    'graceful')
-        netatalk_graceful
+    'condrestart')
+        [ -f /var/lock/subsys/netatalk ] && netatalk_restart || :
         ;;
     *)
-        echo "Usage: $0 {start|stop|restart|reload|status|graceful}"
+        echo "Usage: $0 {start|stop|restart|reload|status|condrestart}"
         exit 2
 esac
 
index 2183f3036f1a4e5b0e054ec5d83d6bbdd9be233c..670f1445c8c10c105c9e5ba297ad70c61ea19dec 100644 (file)
@@ -2,12 +2,16 @@
 
 [Unit]
 Description=Netatalk AFP fileserver for Macintosh clients
+Documentation=man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)
+Documentation=http://netatalk.sourceforge.net/
 After=syslog.target network.target avahi-daemon.service
 
 [Service]
 Type=forking
 GuessMainPID=no
 ExecStart=:SBINDIR:/netatalk
+PIDFile=:PATH_NETATALK_LOCK:
+ExecReload=/bin/kill -HUP $MAINPID
 Restart=always
 RestartSec=1
 
index ade949aa9484fb06c410eaf6ec495e6885e769df..c8189ad19eff21550c168c583c8fda78a0752d30 100644 (file)
@@ -1,4 +1,4 @@
 Makefile.in
 Makefile
-.gitignore
-*.o
+html.xsl
+man.xsl
\ No newline at end of file
index 4a1e7de1ce6ac562ed49d931f4394702208c8c31..2ddb69f6fc410e91078436ff7b3ca81406592300 100644 (file)
@@ -288,3 +288,54 @@ filenames for the usock_file parameter.
 
 - There is no protection against a malicious user connecting to the
 cnid_dbd socket and changing the database.
+
+Documentation
+=============
+Netatalk documentation is in Docbook XML format. In order to build manpages
+and the html documentation from the Docbook docs you need the following:
+
+1. Install `xsltproc`
+
+2. Get the latest Docbook XSL stylesheet distribution from:
+   https://sourceforge.net/project/showfiles.php?group_id=21935
+   Tested docbook-xsl stylesheet version is 1.75.2.
+
+3. Fix indexterm bug in xsl stylesheet:
+   inside the xsl stylesheet distribution in manpages/inline.xsl remove these lines:
+
+        <!-- * indexterm instances produce groff comments like this: -->
+        <!-- * .\" primary: secondary: tertiary -->
+        <xsl:template match="indexterm">
+          <xsl:text>.\" </xsl:text>
+          <xsl:apply-templates/>
+          <xsl:text>&#10;</xsl:text>
+        </xsl:template>
+
+        <xsl:template match="primary">
+          <xsl:value-of select="normalize-space(.)"/>
+        </xsl:template>
+
+        <xsl:template match="secondary|tertiary">
+          <xsl:text>: </xsl:text>
+          <xsl:value-of select="normalize-space(.)"/>
+        </xsl:template>
+
+4. Add the following argument to configure
+   --with-docbook=PATH_TO_XML_STYLESHEET_DIR
+
+5. The manpages and html documentation are now automatically built when running `make html`.
+
+Editing Docbook Sources
+-----------------------
+Free WYSIWYG editor with only one minor drawback is XMLEditor from XMLmind:
+http://www.xmlmind.com/xmleditor/persoedition.html
+
+Drawback: in order to be able to edit any of the  nested xml files, you have to
+"promote" them to valid Docbook files by referencing the Docbook DTD, insert as line 2+3:
+       
+       <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+       "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+These changes will however prevent XMLeditor from opening the master xml file
+manual.xml. Before further processing can be done these changes then have to be
+reverted for any changed file.
index a6fcda87c26eefbfa516d8a595f87943f11a3cb7..e1b356f1a8b62786d6131a5023817d902ef90c66 100644 (file)
@@ -1,3 +1,9 @@
-# Makefile.am for INSTALL/
+SUBDIRS = manpages manual
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+XHTML_STYLESHEET=$(top_srcdir)/doc/html.xsl
 
-EXTRA_DIST = DEVELOPER
+htmldir = $(prefix)/share/doc/@PACKAGE@
+dist_html_DATA = @PACKAGE@.html
+
+DISTCLEANFILES = html.xsl man.xsl
diff --git a/doc/gfx_and_css/logo.ai b/doc/gfx_and_css/logo.ai
new file mode 100644 (file)
index 0000000..0534656
--- /dev/null
@@ -0,0 +1,687 @@
+%PDF-1.5\r%âãÏÓ\r
+1 0 obj\r<</Metadata 458 0 R/Pages 2 0 R/OCProperties<</D<</RBGroups[]/ON[15 0 R 23 0 R 41 0 R 49 0 R 67 0 R 75 0 R 93 0 R 100 0 R 126 0 R 180 0 R 234 0 R 292 0 R 350 0 R 408 0 R]/OFF[119 0 R]/Order 407 0 R>>/OCGs[15 0 R 23 0 R 41 0 R 49 0 R 67 0 R 75 0 R 93 0 R 100 0 R 119 0 R 126 0 R 180 0 R 234 0 R 292 0 R 350 0 R 408 0 R]>>/Type/Catalog>>\rendobj\r458 0 obj\r<</Subtype/XML/Length 25612/Type/Metadata>>stream\r
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.277092, Fri Feb 23 2007 14:16:18        ">
+   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+      <rdf:Description rdf:about=""
+            xmlns:dc="http://purl.org/dc/elements/1.1/">
+         <dc:format>application/pdf</dc:format>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xap="http://ns.adobe.com/xap/1.0/"
+            xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/">
+         <xap:CreatorTool>Adobe Illustrator CS3</xap:CreatorTool>
+         <xap:CreateDate>2009-06-03T14:56:54-07:00</xap:CreateDate>
+         <xap:ModifyDate>2009-06-04T10:49:53-07:00</xap:ModifyDate>
+         <xap:MetadataDate>2009-06-04T10:49:53-07:00</xap:MetadataDate>
+         <xap:Thumbnails>
+            <rdf:Alt>
+               <rdf:li rdf:parseType="Resource">
+                  <xapGImg:width>256</xapGImg:width>
+                  <xapGImg:height>256</xapGImg:height>
+                  <xapGImg:format>JPEG</xapGImg:format>
+                  <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW&#xA;du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT&#xA;l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj&#xA;PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9&#xA;Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A&#xA;VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ&#xA;IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8&#xA;Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man&#xA;9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd&#xA;II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8&#xA;X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l&#xA;jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM&#xA;VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN&#xA;ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj&#xA;F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk&#xA;Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9&#xA;/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8&#xA;1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd&#xA;WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG&#xA;D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui&#xA;rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+&#xA;bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3&#xA;Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd&#xA;YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8&#xA;TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P&#xA;gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA&#xA;dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu&#xA;w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK&#xA;s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma&#xA;hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t&#xA;dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q&#xA;7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz&#xA;fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx&#xA;aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz&#xA;KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr&#xA;G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5&#xA;5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu&#xA;DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE&#xA;dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0&#xA;b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG&#xA;syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW&#xA;8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M&#xA;aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz&#xA;DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj&#xA;irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx&#xA;V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF&#xA;PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE&#xA;v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI&#xA;qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2&#xA;KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ&#xA;vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp&#xA;xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C&#xA;81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5&#xA;Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd&#xA;+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/&#xA;ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX&#xA;UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/&#xA;AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k&#xA;9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx&#xA;Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4&#xA;o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa&#xA;KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ&#xA;/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW&#xA;o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf&#xA;lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1&#xA;Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na&#xA;SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc&#xA;Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0&#xA;4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg&#xA;k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr&#xA;+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD&#xA;6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM&#xA;nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn&#xA;v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu&#xA;/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL&#xA;79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB&#xA;1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk&#xA;rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw&#xA;zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf&#xA;/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt&#xA;iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+&#xA;r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb&#xA;Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L&#xA;dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te&#xA;SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1&#xA;Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/&#xA;AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8&#xA;/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o&#xA;pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW&#xA;+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2&#xA;5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s&#xA;8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC&#xA;ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/&#xA;8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p&#xA;8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq&#xA;wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A&#xA;Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu&#xA;+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA&#xA;8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h&#xA;f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7&#xA;uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK&#xA;t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n&#xA;cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2&#xA;7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX&#xA;2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX&#xA;u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z&#xA;9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc&#xA;8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba&#xA;qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69&#xA;piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH&#xA;/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq&#xA;ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK&#xA;se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/&#xA;1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A&#xA;kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd&#xA;5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW&#xA;H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2&#xA;vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd&#xA;5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn&#xA;/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV&#xA;fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5&#xA;x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13&#xA;y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A&#xA;bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/&#xA;/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4&#xA;NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x&#xA;i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM&#xA;AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY&#xA;q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp&#xA;flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L&#xA;wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+&#xA;RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP&#xA;OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV&#xA;fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q&#xA;8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT&#xA;W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE&#xA;tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo&#xA;6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+&#xA;Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A&#xA;yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5&#xA;OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn&#xA;/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6&#xA;ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5&#xA;f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+&#xA;f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7&#xA;A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY&#xA;UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q&#xA;pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp&#xA;HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf&#xA;9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv&#xA;y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380&#xA;tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl&#xA;djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK&#xA;on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr&#xA;9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/&#xA;HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn&#xA;fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV&#xA;2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8&#xA;66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A&#xA;oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3&#xA;/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj&#xA;8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7&#xA;UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm&#xA;Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h&#xA;wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV&#xA;REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/&#xA;8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl&#xA;zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH&#xA;NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/&#xA;AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq&#xA;2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0&#xA;r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr&#xA;Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/&#xA;AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd&#xA;kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN&#xA;/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/&#xA;AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q&#xA;ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl&#xA;pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx&#xA;+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5&#xA;oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl&#xA;pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+&#xA;kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8&#xA;j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM&#xA;akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI&#xA;rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib&#xA;r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr&#xA;p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE&#xA;v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d&#xA;FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN&#xA;Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV&#xA;dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0&#xA;NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a&#xA;1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA&#xA;6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+&#xA;fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8&#xA;Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N&#xA;j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q&#xA;4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF&#xA;7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu&#xA;IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/&#xA;zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK&#xA;u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/&#xA;AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3&#xA;/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE&#xA;gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+&#xA;g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m&#xA;aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN&#xA;jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX&#xA;Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81&#xA;a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W&#xA;O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ&#xA;hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV&#xA;vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS&#xA;7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj&#xA;odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN&#xA;B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH&#xA;cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK&#xA;UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj&#xA;/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f&#xA;aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9&#xA;WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L&#xA;Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB&#xA;V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G&#xA;1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7&#xA;+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/&#xA;u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq&#xA;+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq&#xA;eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+&#xA;cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G&#xA;Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz&#xA;svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35&#xA;V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv&#xA;JvlTyvam18vaTa6XCQA/1aJUZ6dDI4HNz7sTiqc4q7FXYq7FXYq7FXYq7FXYq7FX/9k=</xapGImg:image>
+               </rdf:li>
+            </rdf:Alt>
+         </xap:Thumbnails>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/">
+         <xapMM:DocumentID>uuid:1B95FEC551E111DEA13AAE68EE5F3A4D</xapMM:DocumentID>
+         <xapMM:InstanceID>uuid:58ad672b-c584-4acf-894c-9cb9832873ef</xapMM:InstanceID>
+         <xapMM:DerivedFrom rdf:parseType="Resource"/>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+            xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+            xmlns:xapG="http://ns.adobe.com/xap/1.0/g/">
+         <xapTPg:NPages>1</xapTPg:NPages>
+         <xapTPg:HasVisibleTransparency>True</xapTPg:HasVisibleTransparency>
+         <xapTPg:HasVisibleOverprint>False</xapTPg:HasVisibleOverprint>
+         <xapTPg:MaxPageSize rdf:parseType="Resource">
+            <stDim:w>3.000000</stDim:w>
+            <stDim:h>3.000000</stDim:h>
+            <stDim:unit>Inches</stDim:unit>
+         </xapTPg:MaxPageSize>
+         <xapTPg:PlateNames>
+            <rdf:Seq>
+               <rdf:li>Black</rdf:li>
+            </rdf:Seq>
+         </xapTPg:PlateNames>
+         <xapTPg:SwatchGroups>
+            <rdf:Seq>
+               <rdf:li rdf:parseType="Resource">
+                  <xapG:groupName>Default Swatch Group</xapG:groupName>
+                  <xapG:groupType>0</xapG:groupType>
+               </rdf:li>
+            </rdf:Seq>
+         </xapTPg:SwatchGroups>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
+         <illustrator:Type>Document</illustrator:Type>
+      </rdf:Description>
+   </rdf:RDF>
+</x:xmpmeta>
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                           
+<?xpacket end="w"?>\rendstream\rendobj\r2 0 obj\r<</Count 1/Type/Pages/Kids[5 0 R]>>\rendobj\r15 0 obj\r<</Intent 16 0 R/Usage 17 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r23 0 obj\r<</Intent 24 0 R/Usage 25 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r41 0 obj\r<</Intent 42 0 R/Usage 43 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r49 0 obj\r<</Intent 50 0 R/Usage 51 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r67 0 obj\r<</Intent 68 0 R/Usage 69 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r75 0 obj\r<</Intent 76 0 R/Usage 77 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r93 0 obj\r<</Intent 94 0 R/Usage 95 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r100 0 obj\r<</Intent 101 0 R/Usage 102 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r119 0 obj\r<</Intent 120 0 R/Usage 121 0 R/Name(Layer 1)/Type/OCG>>\rendobj\r126 0 obj\r<</Intent 127 0 R/Usage 128 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r180 0 obj\r<</Intent 181 0 R/Usage 182 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r234 0 obj\r<</Intent 235 0 R/Usage 236 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r292 0 obj\r<</Intent 293 0 R/Usage 294 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r350 0 obj\r<</Intent 351 0 R/Usage 352 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r408 0 obj\r<</Intent 409 0 R/Usage 410 0 R/Name(Layer 2)/Type/OCG>>\rendobj\r409 0 obj\r[/View/Design]\rendobj\r410 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r351 0 obj\r[/View/Design]\rendobj\r352 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r293 0 obj\r[/View/Design]\rendobj\r294 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r235 0 obj\r[/View/Design]\rendobj\r236 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r181 0 obj\r[/View/Design]\rendobj\r182 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r127 0 obj\r[/View/Design]\rendobj\r128 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r120 0 obj\r[/View/Design]\rendobj\r121 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r101 0 obj\r[/View/Design]\rendobj\r102 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r94 0 obj\r[/View/Design]\rendobj\r95 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r76 0 obj\r[/View/Design]\rendobj\r77 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r68 0 obj\r[/View/Design]\rendobj\r69 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r50 0 obj\r[/View/Design]\rendobj\r51 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r42 0 obj\r[/View/Design]\rendobj\r43 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r24 0 obj\r[/View/Design]\rendobj\r25 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r16 0 obj\r[/View/Design]\rendobj\r17 0 obj\r<</CreatorInfo<</Subtype/Artwork/Creator(Adobe Illustrator 13.0)>>>>\rendobj\r407 0 obj\r[408 0 R]\rendobj\r5 0 obj\r<</Parent 2 0 R/Contents 452 0 R/BleedBox[0.0 0.0 216.0 216.0]/PieceInfo<</Illustrator 401 0 R>>/ArtBox[16.0 18.25 200.0 202.25]/Group 453 0 R/MediaBox[0.0 0.0 216.0 216.0]/Thumb 457 0 R/TrimBox[0.0 0.0 216.0 216.0]/Resources<</XObject<</Fm0 425 0 R/Fm1 431 0 R/Fm2 441 0 R/Fm3 447 0 R>>/Properties<</MC0 408 0 R>>/ExtGState<</GS0 416 0 R/GS1 448 0 R>>>>/Type/Page/LastModified(D:20090604104953-07'00')>>\rendobj\r452 0 obj\r<</Length 1187/Filter/FlateDecode>>stream\r
+H\89\94WÝnå4\10¾ÏSø\ 5âzl\8f=¾¥\v\­Ð\8a\v\1e bAb\vê®\84ÄÛóÍ8q\9c\9cs\8aPÕ&ãùûæÏ\99>ýôì\9e>>\a÷Ý\87g·¼-ÁE*ö»ê\9f¯¿.¿¸?qj?¾&v\7f,O?þ\1cÜoß\967GvN\8e\828jâ%\ 5r/¯&ÿº¬Y|\8b\8ax®Í­©ùÂrÐú\14÷²\8c\ 3JÕ3\8cìº+Õê\89\9b\1aÙ__\96+óª}5\7fq?péÏËòyù´EH\88n\8aJ\92¯9EW¢\97*m\8a\8c|FÈÙÇ\88D\15H\15\10Ì\8aÀ\88â\93d\8d\8b\82o\92\1c\15Ï\ 5\0\1aÎ\9bK    T\85pó"Ñåâc5áê3Þ\98\ 4\83\81\9dàoPäÙ×\96\1dÁ^Ð\f¬)xÎÙ  \ 2VvD\1eØU\81\ e\fçäIª+ð\9a\1cpP\\8d¾Uä\8d\9b' ¬ìK\89\b\0F²Ó\æ¨Ò %²\1eD¸X\8bx
\8f0ªd*¢dJ¤â5ø\92\93\1e¨mõA¦,\ 5ÊðAµ\93Ü¥\11d\9aøÍ×Pwe!¤\8b\12\81¡\fß 9æ\81\fd\97Þ\80\93h\9h~Cð\19Y\ 3@M\1ehønÉ\95ìS5ó\14\918Õè5\     %\89\88¥©¢ÑÙ7h\10!*\1c¨
+\a3\ 2ßÅzPK\ 3\15Î\e\1d³ñ3\ f\8dTÌh\ eæ\16´\18Mb\12fAPw\19¨2Ù\ 1Ê\974\f4H«\11ó\17t.     í\9b0Cè¾M\1a)\14ív$Öø=j ñ\82aøg±£Xy\1c}±Æ\f5Ï'hF.ÓAïñµ7¹öýßÓ¤\84Ó¤DÌ\15\1aºa\ 6ÐìcL\82KäÑH_\96\82«!\r\8aì\89n?\88ÁB\a\92A_1ITíµ\987ÊãU\ f\7f7t\a&F/°ÀMds>PaD\13ra6û+&\98J\ 1\ro9Þ\90È\88\82\a\a²\1d °\9a³+ÝcI¸EøBì\81\86ãõ\1a\b\ 6\8a9ßË."\v¢Y@\91*\9aáJ\8f,¢y3_É!-z|¦î%\94P0°\1eÀHÝ­\96èJÏ~oÉ!\8d[¦Ü\90w\81dd\ 4ß\1c6ýùRÖ®\8a¸:Ìy\9cZ\ 3\83l\94>SÊ*\8d«\0Ú\98÷FZëlj\83ljk\97Fy¬\85fZÙì+wOjj\90é¬{\ 3\9f\93\ 1\7f<-ç\19èT9%íLÝMRÁ<Ãÿÿó\82ø5\14µ|ÔI\87»\1cdÖ0{/\18S\13\16ú k
+Ãa,m¥8\86ø\ 6%nh*!:»xc\9cË\89/ièÃ\16{A   ÃV¦\ 3-+ʦ\9fà\bWVU½\8b\9bCm¥ß\ eµ\f\ 2íÂCÔªF5M4:DW\bý.ÕÞ¼l\1f\91qp1ÐcÁm_ðE¦Ä&\86OKl\98<\·¢ÛÓç¥\81Ñd\bÀJÁÇt\12Ð\1ch\15\87\ 5\9fÿ\93\84í\ 2C\800Ýõ,P4{\87\ 4\93Ä\9bnm¤[ÛÓ\ f¯Á}øk\86\8eU\ 2yz\az\17x\ fú&ñ\18ú&ð\ eôMâ}èÔ¡SCw 
+X\8fZ@\11±\ 6\85\98Ô\7f¬u\ 3\8f¯"°l\12
+\9dÏ|Dß\ 6¿\ 3?ñ     K\1aíü\8e\vv¹¶³;è\99}B\1dϨ±]\90\1f£Þ$\1e¢Þø\ fQoüG¨7ö\7f N\1dõÝÕØ\16~`ÑåiZø±±´¾ðc0\81cÅB^ëAÚÓVµýDëÞ°}îº+a¿¬1Ù¿½báWf<N®Ú7öÏþ7õ}áWK\9bä«\11:7\84\r\v«\9d\90ý_Q\8c\e\8e×¾DËÄ\gÍu²©\8b((Ýò\19Ï®V}ÁÒ\16¶ç˲¹êä$~²£\17Ì÷\1fñ\8fÙ§å_\ 1\ 6\0\1dï§\\rendstream\rendobj\r453 0 obj\r<</I false/K false/CS/DeviceCMYK/S/Transparency>>\rendobj\r457 0 obj\r<</Length 209/Filter[/ASCII85Decode/FlateDecode]/BitsPerComponent 8/ColorSpace 455 0 R/Width 27/Height 27>>stream\r
+8;Wj7_%"=*$tC/]^3R!p2\5ei:.#<:*2umX&QHWQSX?kZ@O:XR!2NU",:A&.IZ])d
+4sFGTN1#]GN(;N>&A>I\_UN-X/[rs;\^P(tkhZ2Z7Hcmh,559!+mO$N-ilCO;X7ru
+%4su8Hf\-trRT+fp'uERM?YVbeK1tUK;H$F2;_7f40XmgrX\d.MSJ41ioUrN`kD>a
+XoSNdk^<4~>\rendstream\rendobj\r416 0 obj\r<</OPM 1/BM/Normal/CA 1.0/OP false/SMask/None/ca 1.0/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r448 0 obj\r<</OPM 1/BM/Normal/CA 0.5/OP false/SMask/None/ca 0.5/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r425 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 414 0 R/Resources<</Shading<</Sh0 422 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[106.997 139.145 150.43 135.867]>>stream\r
+q
+150.43 135.867 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 137.5058594 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r431 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 428 0 R/Resources<</Shading<</Sh0 422 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[106.997 133.854 150.43 130.576]>>stream\r
+q
+150.43 130.576 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 132.2148438 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r441 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 434 0 R/Resources<</Shading<</Sh0 437 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[39.6411 84.9033 88.6143 81.626]>>stream\r
+q
+39.641 84.903 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 83.2646484 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r447 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 444 0 R/Resources<</Shading<</Sh0 437 0 R>>/ColorSpace<</CS0 418 0 R>>/ExtGState<</GS0 416 0 R>>>>/BBox[39.6411 90.1943 88.6143 86.917]>>stream\r
+q
+39.641 90.194 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 88.5556641 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r444 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r418 0 obj\r[/DeviceN[/Black]/DeviceCMYK 419 0 R 420 0 R]\rendobj\r419 0 obj\r<</Length 91/FunctionType 4/Filter/FlateDecode/Domain[0.0 1.0]/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>stream\r
+H\89ª6Ô3\0\ 3\ 5#\ 5C\85¢ü\9c\1c\ 5bD\f\142óRR+\102\ÉeE
\15É\19
+Å¥I\b
+ºèf (4\ 5\ 2Ë\9b\10Pi\ 2\18S!ªåÆ0\95F\ 4\8c4\82[^\90_ P\v\10`\0.§G±\rendstream\rendobj\r420 0 obj\r<</Subtype/NChannel/Process 421 0 R>>\rendobj\r421 0 obj\r<</Components[/Cyan/Magenta/Yellow/Black]/ColorSpace/DeviceCMYK>>\rendobj\r437 0 obj\r<</ColorSpace 418 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 438 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r438 0 obj\r<</FunctionType 3/Encode[1.0 0.0 0.0 1.0]/Domain[0.0 1.0]/Functions[439 0 R 440 0 R]/Bounds[0.967026]>>\rendobj\r439 0 obj\r<</C0[0.735]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r440 0 obj\r<</C0[0.735]/C1[0.735]/FunctionType 2/N 1.0/Domain[0.0 1.0]>>\rendobj\r434 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r428 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r422 0 obj\r<</ColorSpace 418 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 423 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r423 0 obj\r<</FunctionType 3/Encode[1.0 0.0]/Domain[0.0 1.0]/Functions[424 0 R]/Bounds[]>>\rendobj\r424 0 obj\r<</C0[1.0]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r414 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r455 0 obj\r[/Indexed/DeviceRGB 255 456 0 R]\rendobj\r456 0 obj\r<</Length 428/Filter[/ASCII85Decode/FlateDecode]>>stream\r
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
+VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
+PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>\rendstream\rendobj\r401 0 obj\r<</Private 402 0 R/LastModified(D:20090604104953-07'00')>>\rendobj\r402 0 obj\r<</RoundtripVersion 13/ContainerVersion 11/CreatorVersion 13/AIMetaData 403 0 R/AIPrivateData1 404 0 R/AIPrivateData2 405 0 R/NumBlock 2/RoundtripStreamType 1>>\rendobj\r403 0 obj\r<</Length 860>>stream\r
+%!PS-Adobe-3.0 \r%%Creator: Adobe Illustrator(R) 13.0\r%%AI8_CreatorVersion: 13.0.2\r%%For: (admin) ()\r%%Title: (Netatalk_Logo.ai)\r%%CreationDate: 6/4/09 10:49 AM\r%%BoundingBox: 1168 738 1352 923\r%%HiResBoundingBox: 1168 738.25 1352 922.25\r%%DocumentProcessColors: Black\r%AI5_FileFormat 9.0\r%AI12_BuildNumber: 434\r%AI3_ColorUsage: Color\r%AI7_ImageSettings: 0\r%%CMYKProcessColor: 1 1 1 1 ([Registration])\r%AI3_TemplateBox: 1260.5 827.5 1260.5 827.5\r%AI3_TileBox: 972 472 1548 1206\r%AI3_DocumentPreview: None\r%AI5_ArtSize: 216 216\r%AI5_RulerUnits: 0\r%AI9_ColorModel: 2\r%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0\r%AI5_TargetResolution: 800\r%AI5_NumLayers: 1\r%AI9_OpenToView: 1107 936 4.86 1509 1080 26 1 0 50 75 0 0 1 0 1 0 1\r%AI5_OpenViewLayers: 7\r%%PageOrigin:0 0\r%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9\r%AI9_Flatten: 1\r%AI12_CMSettings: 00.6\r%%EndComments\r\rendstream\rendobj\r404 0 obj\r<</Length 16856>>stream\r
+%%BoundingBox: 1168 738 1352 923\r%%HiResBoundingBox: 1168 738.25 1352 922.25\r%AI7_Thumbnail: 128 128 8\r%%BeginData: 16714 Hex Bytes\r%0000330000660000990000CC0033000033330033660033990033CC0033FF\r%0066000066330066660066990066CC0066FF009900009933009966009999\r%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66\r%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333\r%3333663333993333CC3333FF3366003366333366663366993366CC3366FF\r%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99\r%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033\r%6600666600996600CC6600FF6633006633336633666633996633CC6633FF\r%6666006666336666666666996666CC6666FF669900669933669966669999\r%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33\r%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF\r%9933009933339933669933999933CC9933FF996600996633996666996699\r%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33\r%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF\r%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399\r%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933\r%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF\r%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC\r%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699\r%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33\r%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100\r%000011111111220000002200000022222222440000004400000044444444\r%550000005500000055555555770000007700000077777777880000008800\r%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB\r%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF\r%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF\r%524C45FD35FFA87D52522727FD09F8272752527D7DA8A8FD64FFA87D2727\r%FD19F8527DA8A8FD5CFF7D52FD21F827277DA8FD56FF7D52FD28F8277DA8\r%FD50FFA852FD2DF82752A8FD4CFF7D27FD14F827F827F8272727F827F827\r%FD13F8277DFD48FF7D27FD0FF8FD04275227522752275227522752275227\r%5227522727F827FD0FF87DFD44FF7DFD0FF8FD042752FD132752FD0627FD\r%0DF8277DFD40FFA827FD0BF8272752275227522752275227522752275227\r%522752275227522752275227522752275227522727F827FD0AF827A8FD3D\r%FF52FD0CF827275227272752272727522727275227272752272727522727\r%2752272727522727275227272752FD0427FD0BF852A8FD39FF7DFD0BF827\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522727FD09F8277DFD36FFA852FD0A\r%F82752FD2F27522727FD0AF827FD34FF7D27FD09F8272752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522727FD09F8A8FD31FF52FD0AF852\r%272727522727275227272752272727522727275227272752272727522727\r%27522727275227272752272727522727275227272752272727522727FD09\r%F87DFD2FFF27FD08F8272752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522752FD09F852FD2DFF27FD08F8272752FD3E27FD\r%09F827A8FD2AFFFD09F85227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522727FD07F827A8FD27FFA8FD09\r%F85227272752272727522727275227272752272727522727275227272752\r%272727522727275227272752272727522727275227272752272727522727\r%275227272752272727522727FD08F8A8FD25FFA8FD07F827275227522752\r%275227272752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%2752275227522727FD08F8A8FD23FFA8FD08F82752FD0727F8F8FD4127FD\r%08F8A8FD22FFFD07F82727522752275227522727F8272752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752275227522752275227522752272727522752275227522752FD07\r%F827A8FD20FFFD07F8FD0427522727275227F8F8F8272727522727275227\r%272752272727522727275227272752272727522727275227272752272727\r%5227272752272727522727275227272752F8F8F8522727275227272752FD\r%07F827A8FD1EFF27FD06F82727522752275227522727F8F8F85227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752275227522752275227522752275227522727F8F8F85227522752\r%27522752FD07F827FD1DFF27FD06F8FD08275227FD05F8FD3927FD04F8FD\r%082752FD07F852FD1BFF7DFD06F82727522752275227522727FD05F85227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522752FD05F82727\r%52275227522752FD07F87DFD19FF7DFD07F8275227272752FD0427FD05F8\r%272752272727522727275227272752272727522727275227272752272727\r%52272727522727275227272752272727522727275227272752FD0427FD05\r%F8272752272727522727FD07F8A8FD18FF27FD05F8272752275227522752\r%2727FD06F827522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752FD07F8272752275227522727FD07F8FD17FF52FD06F82752FD0827\r%FD07F8FD3B27FD07F8FD0927FD06F827FD15FFA8FD06F827522752275227\r%522752FD07F8272752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227FD08F8522752275227522727FD06F87DFD14FFFD07F82727\r%52272727522727FD09F85227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%272752272727522727FD09F85227272752FD0427FD06F8A8FD12FF52FD06\r%F852275227522752275227FD08F827275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522752275227FD0AF8522752275227522727FD05F852\r%FD11FF7DFD06F8FD0B27FD09F8FD3B27FD0BF852FD0627FD07F8A8FD10FF\r%27FD05F82727522752275227522727FD09F8272752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227FD0AF8272752275227522752\r%27FD05F827FD0FFF7DFD06F827272752272727522727FD0BF82727522727\r%275227272752272727522727275227272752272727522727275227272752\r%2727275227272752272727522727275227272752FD0427FD0BF8FD042752\r%FD0427FD06F87DFD0EFF27FD05F82752275227522752275227FD0AF82727\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522752275227FD0C\r%F8522752275227522727FD05F827FD0DFF52FD06F8FD0B27FD0BF852FD3A\r%27FD0DF852FD0727FD06F87DFD0CFF27FD05F82727522752275227522752\r%FD0BF8272752275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227FD0CF827275227522752275227FD05F827FD0BFF7DFD05F8FD0427\r%52272727522727FD0DF85227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%272752272727522727FD0DF8FD04275227272752FD06F87DFD0AFF27FD05\r%F8275227522752275227522727FD0BF82727522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227FD0EF8522752275227522727FD05\r%F827FD09FF7DFD06F8FD0B27FD0DF8FD3A27FD0FF8FD0A27FD05F8A8FD08\r%FF52FD05F8272752275227522752275227FD0CF827275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752FD0FF85227522752275227\r%5227FD05F852FD08FFFD06F82727275227272752FD0427FD0DF827275227\r%272752272727522727275227272752272727522727275227272752272727\r%5227272752272727522727275227272752272727522727FD10F827522727\r%275227272752FD06F8A8FD06FF7DFD06F85227272752272727522752FD0E\r%F8275227522727275227272752272727522727275227522752FD0B275227\r%522752275227522752275227522752275227522727FD0FF8272752275227\r%5227522727FD05F87DFD06FF52FD05F8FD05522752525227525227F827F8\r%27F827F827F827F827F827525227525252275252522752525227FD055227\r%522727FD07F827F827F8FD04275227272752FD0C27FD11F8FD0A27FD05F8\r%27FD06FFFD05F852FD29FFA87DFFFFFF7D7DFFFF27FF52A8527D527DFD0E\r%52275227522752FD0427FD11F82727522752275227522752FD05F827A8FD\r%04FF7DFD05F87DFD29FF7D7DFFFFFF7D52FFFF27FF7D7D52527D527DFD06\r%5227522752275252522752FD0627FD13F827275227272752FD0427FD05F8\r%7DFD04FF52FD05F8FD0D52FD1027FD0C522727F8272727F8272727F827F8\r%27F827F827F827F827F827F827F827F8F8F827275227522752275227FD12\r%F8272752275227522752275227FD05F852FD04FFFD05F827FD047D527D7D\r%7D52FD057DFD0F527D7D7D527D7D7D527D7D7D5252272752525227F87D52\r%2752FD0727F827F827F827F827F827F827F8FD0927FD13F852FD0A27FD05\r%F827FFFFFFA8FD05F87DFD2AFFA852FFFFFF7D7DFFFF27FF7DA8527D7D7D\r%527D527DFD0652275227522727275227522727F827F827FD0FF827275227\r%522752275227522727FD05F8A8FFFF7DFD05F87DA8FFA8A8A8FFA8A8A8FF\r%A8A8A8FFFD13A8FFA8FFFD05A8FF5252A8A8A85227FFA827A8527DFD0552\r%275227522752FD0827F827F8272727F827FD12F827522727275227272752\r%272727FD05F87DFFFF52FD04F827275227522752275227522752275227FD\r%12F827275227FD2AF827FD15F827275227522752275227522752FD05F852\r%FFFF27FD05F8FD0F27FD13F827FD42F8FD0D27FD06F8FFFFFD05F8272752\r%2752275227522752275227522727F8F8F827F8F8F827FD07F827FD07F827\r%FD08F8527D527D527D527D27FD06F827F8F8F827F8F8F827FD07F827F8F8\r%F827FD07F827FD08F82752275227522752275227522752FD05F827FFA8FD\r%06F852277DA8FFA8FF522727A8A8FFA8FF27F8F8FD08A8FF27F8FD0BA8FF\r%27FD06F8FD08FF52F8F8F8FD0CA852F8F827FFFD07A827F8F8F827FD05A8\r%7DFD05F8FD04A8FF7D2727FD04A8FF7D27FD05F8A87DFD05F82727277DFD\r%04FFA82752A8FD04FF52F827FD09FF52F8FD0CFF52FD05F87DFD08FF27F8\r%F827FD0CFF52F8F852FD08FF27F8F8F827FD05FF7DFD04F827FD06FF2752\r%FD05FF7D27FD05F87D52FD05F82727F87DFD05FF5227A8FD04FF52F8F8FD\r%09FF27F8A8FD0BFF27FD05F87DFD08FF27F8F8F8FD0CFF7DF8F852FD08FF\r%52F8F8F827A8FD04FF7DFD05F8FD05FF7D277DFD05FF2727FD05F85252FD\r%05F85227277DFD05FF7D27A8FD04FF522727FD09FF52F8FD0CFF7DFD04F8\r%27FD09FF27F8F827FD0CFF7DF8F8A8FD08FF7DF8F8F827FD05FF7DFD04F8\r%27FD05FFA827FD05FF7D272727FD04F85227FD04F8FD04277DFD05FFA827\r%A8FD04FF5227F8FD05FFA852527D27F8527D52A8FD05FF7D527D27FD04F8\r%52FD09FF27F8F8F852527DA8FD05FF7D7D5227F8F8A8FD08FFA8F8F8F827\r%FD05FF7DFD04F827FD05FF7D52FD05FF522752FD05F82727FD05F8522727\r%7DFD06FF52A8FD04FF522727A8FD04FF7DFD08F87DFD05FFFD08F87DFD09\r%FF27FD06F87DFD05FF27FD05F8FD09FFA8F8F8F827FD05FF7DFD04F852FD\r%05FFA87DFD05FF27522727FD04F827FD05F8FD04277DFD06FF52A8FD04FF\r%272727FD05FF7DFD08F8A8FD05FF27FD07F8FD05FF7DFFFFFFA827FD06F8\r%7DFD05FFFD05F827A8FFFFFFA8FD05FFF8F8F827FD05FF7DF8F8F82727FD\r%05FFA8A8FD04FF7D272727FD05F827FD06F85227277DFD06FFA8A8FD04FF\r%522752FD05FF7DFD08F8A8FD05FF27FD06F852FD04FFA8A8FD04FF27FD06\r%F8A8FD05FF27FD04F827FD05FFA8FD04FF52F8F827FD05FF7DF8F8272752\r%FD0BFF7D27522727FD0AF8272752277DFD06FFA8A8FD04FF522727FD05FF\r%A87D527DFD05F8A8FD05FF27FD06F87DFD04FF7D7DFD04FF27FD06F87DFD\r%05FF27FD04F852FD04FF7D7DFD04FF52F8F827FD05FF7DF8F8272727FD0A\r%FFA8FD0427FD0BF8275227277DFD0CFF522752FD09FF27FD04F87DFD05FF\r%27FD06F8FD05FF27A8FD04FFFD07F87DFD05FF27FD04F852FD04FF7D52FD\r%04FF7DF8F827FD05FF7DF8F8522752FD0AFF7D2727522727FD0AF8272727\r%F87DFD0CFF522727FD09FFFD05F8A8FD04FFA827FD05F827FD04FFA8F8A8\r%FFFFFFA8FD07F87DFD05FFFD05F87DFD04FF5252FD04FF7DF8F827A8FD04\r%FF7DF8FD0427FD0AFF7D27272752FD0BF8275227277DFD0CFF522752FD09\r%FF27FD04F87DFD05FF27FD05F87DFD04FF7DF8FD05FFFD07F87DFD05FF27\r%FD04F8A8FD04FF5227FD05FFF8F827FD05FF7DF827522752FD0AFFA85227\r%522727FD0AF8FD04277DFD0CFF522727FD05FFA8A87DA8FD05F8A8FD05FF\r%27FD05F8A8FD04FF2727FD04FFA8FD07F87DFD05FFFD05F8FD05FF2727FD\r%04FFA827F827FD05FF7DF8FD0427FD0BFF5227275227FD0AF8275227277D\r%FD0CFF522752FD05FFA8FD08F8A8FD05FF27FD04F827FD05FFA8A8FD05FF\r%FD07F87DFD05FF27F8F8F827FD05FF5227FD05FF27F827FD05FF7DF8F827\r%2752FD0BFF7D27522727FD0AF8FD04277DFD04FF7DFD07FF272727FD05FF\r%7DFD08F8A8FD05FF27FD04F852FD0BFFA8FD07F87DFD05FFFD04F827FD05\r%FFA8FD06FF52F827FD05FF7DF8F8F82727FD05FFA8FD05FFA8272727FD06\r%F827FD05F85227277DFD04FF7DA8FD06FF522752FD05FFA8FD08F8A8FD05\r%FF27FD04F8A8FD0CFFFD07F8A8FD05FF27F8F8F852FD0CFF52F827FD05FF\r%7DFD04F852FD05FFA8A8FD05FF52522727FD05F827FD04F8272752277DFD\r%04FF52A8FD06FF522727FD05FF7DFD08F8A8FD05FF27F8F8F827FD0CFFA8\r%FD07F87DFD05FF27F8F8F852FD0CFF7DF827FD05FF7DFD04F827FD05FF7D\r%7DFD05FF7D2727FD05F82752FD05F85227277DFD04FF5252FD06FF522727\r%FD06FF527D7D52FD04F87DFD05FF27F8F8F852FD05FF7D27A8FD04FFA8FD\r%07F87DFD05FF27F8F8F87DFD0CFFA8F8F8FD05FFA87D527DF827FD05FFA8\r%52FD05FF7D272727FD04F82727FD05F82727F87DFD04FF5227A8FD05FF52\r%27F8FD09FFA8FD04F8A8FD04FFA827F8F8F8A8FD05FF27F8A8FD04FFA8FD\r%07F87DFD05FFFD04F87DFD05FF5227A8FD05FFF827A8FFFFFFA8FD04FFF8\r%F8FD05FF7D52A8FD05FF2727FD05F8527DFD05F85227277DFD04FF7D27A8\r%FD05FF52F827FD09FFA8FD04F87DFD05FF27F8F827FD06FFF827A8FD04FF\r%A8FD07F87DFD05FF27F8F8F8FD06FF27F8A8FD05FF27F8FD09FFF8F8FD05\r%FFA827A8FD05FF7D27FD05F8527DFD05F82727277DFD04FF522752FD05FF\r%52F8F8FD09FF7DFD04F8A8FD05FF27F8F827FFA8FFA8FF7DF8F8FFFFFFA8\r%FF7DFD07F87DFD05FFFD04F8A8FD05FFF8F87DFD05FF2727FD08FFA8F8F8\r%FD05FFA8277DFD05FF7D27FD05F87DA8FD05F82727277DFD04FF7D2752FD\r%05FF52F8F8A8FD08FFA8FD04F87DFD05FF27FD04F827F827FD05F827F827\r%F827FD07F87DFD05FF27F8F827FD05FFA8F8F87DFD05FF52F8FD09FFF827\r%A8FD04FFA8277DFD05FFA827FD05F8A8A8FD06F852272727522752FD0527\r%52275227FD55F82752275227272752275227522727FD05F8FFFF27FD04F8\r%2727522752275227522752275227522727FD55F827275227272752272727\r%522727FD05F827FFFF52FD05F8FD04275227272752272727522727FD0DF8\r%27F8F8F827F8F8F827F8F8F827F8F8F827F827F82727F8F8272727F82727\r%27F8272727F8272727F8272727F8272727F8272727F8272727F8272727F8\r%272727F8272727F8272727F82727FD0C52FD05F827FFFF7DFD05F8275227\r%5227522752275227522752FD07275227522752275227FD08527D527D527D\r%5252527DA852FFFF52A8FFFFFF52A8FD38FFA8FD05F87DFFFFA8FD05F8FD\r%0E2752FD0B2752275227FD0C527D527D52FF52FFFF52A8FFFFFF27FD39FF\r%52FD05F8A8FFFFFF27FD05F8522752275227522752275227522727F827F8\r%27F827F827F827F827F827F827F827F827F827F827F827F827F8FD0527F8\r%FD3227FD04527D5252527D52525227FD05F8FD04FF27FD05F82752272727\r%5227272752FD0427F8F8F827F8F8F827F827F827F827F827F827F827F827\r%F827F827F827F827F852F852272727522752F82727522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%27522752527D527D527D527D527D5252FD05F852FD04FF7DFD05F8272752\r%27522752275227522752FD05275227522752275227FD08527D527D527D52\r%FD057DA87DFFFF52A8FFFFFF52FD38FF7DFD05F87DFD04FFA8FD06F8FD0A\r%27522727F8FD0C275227522752275227FD08527D52A827FFFF527DFFA8FF\r%27A8FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF\r%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFF52FD05\r%F8A8FD05FF27FD05F827522752275227522752275227FD04F827F8F8F827\r%F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F827F827F827\r%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827\r%F827F827F827F827F827F827F827F827F82727522752275227522752FD05\r%F852FD06FF7DFD05F827275227272752272727522727FD57F8FD0927FD06\r%F87DFD06FFA827FD05F8522752275227522752275227FD56F82727522752\r%2752275227FD06F8FD08FF27FD05F8FD0C27FD56F82752FD0827FD05F852\r%FD08FFA8FD05F827275227522752275227522727FD55F852275227522752\r%2752FD06F8A8FD09FF27FD05F82727522727275227272752FD56F8275227\r%272752272727FD05F827FD0AFF7DFD06F85227522752275227522727FD55\r%F8522752275227522727FD05F8A8FD0BFFFD06F8FD0727522727FD55F8FD\r%0927FD05F827A8FD0BFF7DFD05F82727522752275227FD58F82727522752\r%2752FD06F87DFD0DFF27FD05F82727522727FD5DF8272727FD07F8FD0EFF\r%7DFD06F8522752FD67F87DFD0FFF27FD06F827FD67F827FD10FFA8FD6EF8\r%A8FD11FF52FD6CF852FD12FFA827FD6BF8FD14FF7DFD6AF87DFD15FF52FD\r%68F852FD17FFFD67F827A8FD17FFA8FD66F87DFD19FF7DFD64F852FD1BFF\r%52FD62F852FD1DFF27FD60F827FD1FFF27FD07F852275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522727FD07F8FD20FFA8FD08F8FD4F27FD07F8A8FD21\r%FFA827FD07F8522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%5227522752275227522752275227522752275227522727FD07F8FD24FF7D\r%FD08F8FD0427522727275227272752272727522727275227272752272727\r%522727275227272752272727522727275227272752272727522727275227\r%27275227272752272727522727275227FD08F8A8FD25FFA827FD07F82727\r%522752275227522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522752275227FD08F8FD28FFA827FD07F8FD4527FD09F8A8FD2A\r%FF27FD07F827275227522752275227522752275227522752275227522752\r%275227522752275227522752275227522752275227522752275227522752\r%275227522752275227522727FD08F827FD2DFF52FD09F8FD042752272727\r%522727275227272752272727522727275227272752272727522727275227\r%27275227272752272727522727275227272752272727522727FD08F827FD\r%2FFF7DFD09F8272752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227522752275227522752275227\r%52275227522727FD08F87DFD31FFA8FD0AF8FD3827FD0AF87DFD34FF52FD\r%0AF827275227522752275227522752275227522752275227522752275227\r%52275227522752275227522752275227522752275227522727FD09F852FD\r%37FF7DFD0AF8FD0427522727275227272752272727522727275227272752\r%2727275227272752272727522727275227272752FD0427FD0BF87DFD3AFF\r%52FD0BF82727522752275227522752275227522752275227522752275227\r%522752275227522752275227522752275227FD0BF852FD3DFFA827FD0CF8\r%FD042752FD2027FD0DF8277DFD40FF7D27FD0DF827275227522752275227\r%52275227522752275227522752275227522752FD0427FD0CF8277DFD44FF\r%7DFD11F8FD042752272727522727275227522752FD0627FD0FF82752FD48\r%FF7D27FD11F827F8FD0B27F827FD12F827A8FD4CFFA827FD2EF8527DFD50\r%FFA87D2727FD27F8527DFD56FFA87D2727FD21F8527DFD5DFFA87D522727\r%FD17F827527DA8FD64FFA8A87D522727FD0BF8272752527D7DFD34FFFF\r%%EndData\r\rendstream\rendobj\r405 0 obj\r<</Length 23765>>stream\r
+%AI12_CompressedDatax\9cì}i_úÈÒèý\ 2|\aP\91\1d\92\10\12\82\8a\10   ¢¸áΦ"«\ 1fÎÿyñ|ö[ÝÙC\12Ârî\9cçÞû\9b\19G;\9dªêêªêªê®´ßWoÄòÝI»\17KÆ     ¯Çï/\88½Ö|"f¼¸Õ{6\1c.fs\115\ 5oB^\12zA§üYú]îxß\13gýÉ8\83\1fÅ)x( ·\83­î¨?\ ey\83!h¹íÏ\87=h»ìÍ[óÖpð^\9b|Mâ­~HÁ\a\0\8a­9ta\12t\82à¼$\91¡9oþ\ 2\9eó\93ŸÛ\1f\7fñ\93\7f\ 1\ e\92I{Ùd\1a\90¥(/G%¡C¥\7fÓ\9b\8aS)¥#\ 5¿Cß⤳\18õÆóº8éôf³Âd8\11g\19/?lu\ 6\1e\18Uê]è\ f{0\80QkîåÐPóg$õÎ/úÃîåbÔîÁÈè$\8d\9a\93ïøí»Yë\vèÆ¿£föýl\ 4-\8dÞ|\ eä\0lÄ®ÂE³ªG      $Êÿ\ 4\9foz_}Ì`àÁkH\82\eM\87À\ fi4\14CÄSÞ4ÅÂOý\1fr_ \18÷ãXÊKÃ\7fd\8a\ 6þP\ 4#=×\86Üû«ßû;ã½\9c\8c{ÒXóâ¼Ñÿ/ \9e"\19ô\9fÔz³\18öÄ»q\7f.\11\9f?ã¤\91^Lº½!ôUß\15\86-<@ü\ f©ý\94:ܶįÞ\1c¦f2\̱\80¤     ù\11p²ÖúÓC\9c'%\ 4WÓÞøvr\8fé#I\82õrIÆKÇÓ\f\8c\ 6\8bC\9aðR\f\ 6\9f"¼lJE\86ÿ\93 "\18\b\82\ 2\9a\ 5Æ×a*®ÄþW\7f\9c\91    cßËb¿«M\ f0,-ýÀ´ÇÓºÿ8å?\89H\18ï|Þ\eËD\83X\14.tÓLÄ\81\7fþÒ¸[\98\8c\10¿gHvanÇ0ñÃÉ\97ôLý\1d?\81·\17S\89xü÷;LM]ì\8f\11HÏ%~\92\ f\17ð¨,N\16Ó³ñçÄ\13\94ô²Þ\9a\7f\83ÜöÆÝ\19(\99Ô&ýé\95Þ\80ÖZÿ¯\9eÔ\ 6º6\r9»\15[\1d@ë½jÿô:sxYnÐ~k,úóÞj@\8d\ eâ\92èåÅÅìÛ{;\99\f\8c\8fT2åfÜ\8aúÿgà¨ã\17ÆWc\89ÓË\98ä\ efL \18ÿqX ·=\ 6\9f\f½Ð\1a\ eû_bkúÝïX!°x®b\92\9e­\81\fT\ìiïã?\95ÿ»\10Ë?£ödØ\9f\8d4iÔµÔ[â¼ß\19ö\1a\7ffóÞh5´bï\13Ö!\1dÛpkiüWo8\99ê\88T[Zã®÷¡%N\9d@£iúì\8f» !X\9f56NFS´\86z\eß­i\ f\93;ÿ\16\86\v1\1e¶Æ-Ñ\8bÛU\90Èü\80ü\82=3\9a$©M\ 5\9az\aè7}±\98\87òòc\9da,\8b­n\1fl*¸\10wãqkÔëz¿ä&XéB\1eËV0ç\0¦ëyö\1c{\ 4A(    E¡ ðB^à\84´À
+\8c\90\12h!)P\ 2)\10%¡T*\15K\85\12\97¸RºÄ\96\98RªD\97\92%ªD\96\88¢P,\15\8bÅB\91\8b\1í)²E¦\98*ÒÅd\91*\92E¢ \14J\85b¡Pà\vù\ 2WH\17Ø\ 2SH\15èB²@\15È\ 2Á\v|\89\ 5\9eçó<ǧy\96\14OóI\9eâI\9eðä\85|)_Ì\17ò|>\9fçòé<\9bgò©<\9dOæ©<\99'8\81+qE®Àñ\\9eã¸4Çr\f\97âh.ÉQ\1cÉ\11i!]J\17Ó\854\9fΧ¹tÚ\93\1f&\9dJÓéd\9aJ\93i\82\15Ø\12[d\v,ÏæY\ e\1e³,æX\9a\14\ 4#0%¦È\14\18\9eÉ3\1c\93fX\86aR\fÍ$\19\8a!=\f\91\12R¥T1UHñ©|\8aK¥Sl\8aI¥Rt
+|¬\14,Ô´@\97è"] y:Ost\9afi\86NÑ4xLI\9a¢I\9aH
+ÉR²\98,$ùdÞ\93ä\92é$\9bd\92)x\9eLRI2IP\ 2\8aT\81â©<ÅQàçP\f\95¢h*IQ\1485¤@Â|\90E²@òd\9eäÈ4É\92\f\99"i2é!)\12\9c\a\ 2¦\93\80\19!x\ 2Ø\ 6\9e\ 3\vÿ\0ñ\ 4 '\0\ 2x!Y\ fáõ¿ó"È\ 6\14Ð\8aZ\1a\1eÿ»®\85\9fy\90W@sq:E0^µ\8b±\11zñEU\92\15Yu)¾´\95ôÒÿ/\v/\88¯'\9dþ÷
+ïzâK\e\ 4&N1)ID8&Î\12ɤ*8ËOv$@\16\8e¡\95Õt|\81\93\9e \9f\7fþgØ\9by\12Õñäï1þÃ\9bñ\ 4\9fa½i-\86ó×\907q      âè\8dz\12\8d>Ä!=¥\vá½ò\10²»?\80ßn<\8aë_õ(\ 1\0á}ü\ 3\7f\9cÃ/?Ðô·\97ö^x\9f_        o\17Z\1fo<(æyìz\12Ò²päñ&\0\1f\13\b#ÒÈ[9Æzk\bÞw\ f\13Qo{ì"*DÒ̳\14gÕ;\9e:¯"Q`­ÃÁ\1a\80Ó³mi\f¸Ã&SE\12Ò#É{@`þ\97Ü\f\80L\8d6ÀeZ\95à\ f\82ë\16\88]Bù\ef\13ýÕï n´Ä?Òß\8f\17µK\bò,\1f\1ey\83ÿ\1a\rÇð8\ 6\1e©Øo/æ=\88<¢¨k^\14[ÿg@ì\0¾®Wç\eÂz±7\96úPÞÄ\19pF}\8a~Ìÿ _\b=\r\1e\8e\7fµÄÙ\11èG\ 3 C¬dèúWk¸Pú¢ö\99M?dê¥n2%3Ã_ÿC¹Óîãô\vé\829ÀD\b|/1\eVsHß;úÏ\8eq<\19Û\91¬\1fßpÒ\19ôºnƦôÜÑôo9zÒqô®æµßj\ f{n\ 4\7fåLþORôÌ_®U\1d\87¥\18\r¯³\98Í'£\7fÖ\92ýûä03k!·\ 5­y`:Ü\8aã¿]/\80\96ÿ RþoÐÒÙçßÿÁ«ñ?¬\ 6³a¿ó?Ý\16Ç\18. |¸éµ\86\ eÓëjjÿi»+oG­\18Ë\1f7cùóO\8f\85d\92é䪡üÝïοÝ\fGîø\ f\ f)M¯\1aÐw¯ÿõíÆ\84«=ÿá!y\13üdâ0 öd\ e^@­÷9\97¶ÈÜ\f\9dÿ\80\15\1f\9bºÆd!vzx\1fø\1f_òaaú§I\18õæ­.x@ÛÒÁmIÇ~WN9¸\91.]g\83\ 4£Ü\87÷SÚ~í\89ÞÉb>ì\8f{ÞÙ\\9c\f\14AcS2¥¦Îbk6ï\89±¿z\9dùDô¶[ÃÖ¸cÖ\10Ó+\9da\7fêíL\90\13ù/¯Øû\ 2\8d\95\91\10+(\9a÷þµ\82ö©Ø\9bõÄ¿zÞÉ_=q\8ar6\16 I¯Â\bo\7f6A'\ 1¼m´\8f\8b÷c1ô\14¡\98«ü\19ç½è;½7x ýÿÂ\19.¯¶ñî\b~0\86\18\14¨÷~I[7¨+J·§UfrÞ\968oOZb\17X2\ 4\16\129hM\12\ 6\16ó)\80r&\84\92){è$PÒë¹íÛV\ 5meW\95hJ×k.¶Æ³i\v¤½ó\a\10÷»ÞYÿ¿z.Ù !_ÙU7&Ò0\11uE\1cnAr¼¥n\7fÞj÷\87ýù\1f\14\99&½jò\ e\9f\eðÖZã¯Eë«ç­O¦ÊÔ\ 5\9f/zÝþb¤ã:ÊçÞ\8dû\1dÐ5Eÿ¬       ª\84b\92æ^]ÊÅ´j%\19eÒ§­n×$^£Öl`\1aèl:\99\9b\86}Em\95éèNûq\99\9br\v0OTMCþÌ\9b_Ì'ªpõ\96Ä\9aóN[S \7fÖ\1f-\86-MàHÛ\891òx\90?«·D\18\9f\95\1e:­ñÙ\1föª½?¦\11\19º¢\9dS`ÙLC¨i\85¡ccÑ\ 6Æ
+\13\e
+¸õ¢\80@CO+\890t}øîw¾ÍÔ\1e!":\13±Ûë.O£7q9\99\e\1e\93zN\8e'\9a}òöÇØ\18Nfx¿ØAÌtòEyaÂ\1cdJ¯\ 5¸o\ 1ÙÜ\82lsoô6WoØqWI\ 4\12÷\92\r\16]o\17¥ÞÊv
\1e\97\f¥É"\19¬\98ôR\r\19stt\ 1½\84UÓü\92Q\8d\86Ã\b\96û^ÉëEC¿\82­æªô²\ 3[õ¼\92:»f\96Ô}]nIo­f×
+á3ì>\99¬UBs)\13 ¶Ã\1e\ 6d\10%<d}¿Ù|\18ïJ\10±\f÷d]\ f:\83G¯Éý5¿ÄÍ;Óî\b\1e\ fÇ®\89\9av]\ 3\97\92\edZa¿¾ãt*Æûèàa|\b¾¹»\9eó\89²às´sOQ\8b\14åÜW\8a\10TcµÜµ\83úþ=ùü\9cõ櫺}\9bº-ñôgÒ\ 6\9eÎú_ã\96Á×±ì\88§¶-G\b+;bkê¢_kÖîÏG­©sW©\8f¸ì\95-õî\fÅxg2\ 6ó;G+§\ 3©¨§j¦Ûè\f«C_±\e\9f\88HµW0
+uü\ 4Sö=\11ÿ˸\1e\9b\15A\ fËb\12\11V pØ\9aÆ¿]öûË\9e¶é×h\10\87\90\ 1\9cawú\83_\90\14H\9bM7ïÀ\94ÍÑ¡e\99\16]êf©+¨4²åº4\86-éSP\90þøs²¢\9b¨Ûª^Ñ\15û1í\968s`.î\b,ëuA\9btöÁEgÕD¸è«3\12.zëÍ\ 4cÛ}Ô\12\a3#Õ.:«T»è«£ÚEo\93q[\9a\93Ïñ<Þ\1dNÅÏÉØIq§³xÇì5Úô\ 3ë\87\82Ç1tÕ­¿K=ÿ5\8d\8b\82×w«\9e_nÌ\10ô\93­\90!¨uè'\85ãö\10Å®83j/iÅqÜM    \98g3{Ì\9dQ\\89¦;£?N¶Oë8\99\7f÷D\a\86Ã\9a#\99\v{´¨\8f¼\8cËrCÚ.\89:£Àr¶½ôù 8B`MºÞö\1foQìÿ\85¨u´Vð¾æv8,¸\ 6ód·Üö\f:¼ÌHÔ ÇÌ®,)Z÷\f´Y®`ÈsZµ4\88\97\ fèó×R\0fî5ë´\86=\ 5ÖÊ~\ eK\91Ôi:ìüq\10Pܧ3\9e\14úÌ!¾S2;¶\96~\86\8e\14­è\83­ëª\15CT¬£\15É\92\ 13L\89e\9f\19IJò¨\92Ö\86kØû«7tТ\19¸D(\1cp´~ãÞ\17¬\82\7f9\8d\e\99RpÖÆÎ}\86d\aÇÒK1\8cÙÜ~·º=±7s°OSÉ:i¶\89µr\90À,\9as\8d\96¦\13<-G\e<\99v\1cÜ\ 5Üaæ0tÜ¡»X';dzÝY\81%c½\18w\\19\ 4Ü»5\1e+ù -¸]êµÊ
+\83Mפ4x\17oĽ\ f½6Äß­9\18Ï\97`ãáªþ\12òþE­\b(ÑÒ óô)kT($ì+ié#5w\87²#y¥£\17\85¸\9b¥í\bÛ\ 4ÁíÒÚ«u1%\90\97R*\96I\ 6®\97\86iE¬n=i8\1c\82aÁâðå ò¨ÛlÐ\9fB´2vX±Q7\11\f\888ë!Ä\ e+6ê\89²\ 2­yÏjvÎ'mT§ \9b\e\1d\1f¥ñ\9bظ2S¡«\88ôî6k\81è®\17\85w%)¬Ã\84\0·Ð\9aJùá~ÏÉãW\0ñh=¸q^Ó\95¾Bk6\a\rB\15t\8e\14R¨s±7\ 3rp\80R\9f\fû\9d?n\89¹uZ|\94\9ee\94Ü\82    ½ý^\8cÚãV\7f¨¸\e·ßÀ\18/*Ñ\ 17Î+;aÞ\99\\84çýû»7öÎZ\7f¡  h\8dõ¥«^¤áÞÖ\f\15+EAÝ\ 5\89{ïf\18$ü4\ 2û3Yx§ ªÞÉØÛ\93¦\0£\96À}µúc\94\1dÕ!\8az\ 1\99úê\18\9dO\10\88NÏÛÇ©Ô\96wØú\83v\ZÓ)pNÚ$\99-:ß\88¼³q\11'340\12¶10u\ 1ÔM>5ôý\99w1\1e 3ÜqgñÓMZGìO\9dÝ~¥3p\ 4kЭÎ7q\9a3U\8d\H\9aäÛßêö^V¿\84KPy5Ð¥R\8c\93\0\89­?¨.¶¡ß'°\1d*\102\9dêü¦\15"\Ó\12l\96¡\98ÒYK÷ãl¿½³¤\rÑ6ÛoÕýVì\8f.P`ºZ]\eóÖ¸Û\12ÝåL\94\97ä\ 3ó\7f.\1d\1d\80\95\e¡«\19Êë\82jçyÅBàjbe;\8a\88I·ÚÉ1×Ê¢X8wF[Ù\ 6Ê\8b\ eÁ²^@\8b ¹ÒIÅj\7f¼ÞÔH\e­\85ɸÛ_©Í\12'`e>Ó\12O6é,³d=èÂAç¡Ã´X\rÇ\89$\10/\9dô®ÒfÝ&\9b\ 6Þ\89&IUû\8a\93a\9d5Q:_LÆ\93η8\19õ´Q\98w0,½|÷g\r,\93KªñÄYn\ 1\b\0éù{"\ et6&e£@®vý\9d\8c\ 2\9eµ\ 2NÝÌLë8m\999ÒsKã\13þÜA¾=ùËYGÜ\9d¯X©üÖràH®Q×\96\8e#¬5/:_\8a¢l\ 4ÂJ%\2I§ÜðXúN\ 5\91\ 5ÒÏ\8f3nã\80]¢68\9eù\8e8i·æÒ\97\146°LKÄo\1a\7f\9a\\ 6x.myj0×\9b¿Uî'%q\ 1Ó}6î\80[gÒ\f\17Òmy:i3Ã~Ö\ 56ö?ûJ\0´jIÑiÇõ¢¥¹\11\ e×gv\9cðZÙNw&Ú¬Ð\ 6\92iW(-ÍÁz"¡÷>\9c\r\89I§ÍL\82À\13zIòYP#>9¨äûJ@\99o\14ÎÎÒ©b\ fÉ\ 2zH\9f^\1d>G\8e\1fN\ 2G­Çèyòð*ÆçÄòè;ó5ö\9d\v¾h0Pè·â³\ 3æ®Rbö3¹»rö\82>ÍÔ^\ 2\179qÑa\85\12u\91ö\934½O\10³âOñ+J\1cä\8eÞâáÜqt:ËͪTÂãÏ\1dÕ|¢Òé|Î\7fU®k¹cº×(ôO²\9db<\1eøZBUë6\ 1\1f[\14ü\19ö©</þ¼òôS,\9a\1fMj³üYcþ\1dÉ2û\v¡H\1f<ð?ÃÀ\83Ç_ü$ÎÛ\96À\ e\93½¿~~Éß\16â÷öHõý2¯¹ã\81ð\9aËÌâ£H1ê_\bÁr÷ÓãÇÌ\12>@[\8a\9f¯\ f,?Ì\r\1f3\9fü÷¼ðÍ>\91\ 6v|\1c\16;dí7w|\1ax\90à\0ɳÂÛ×Û\ 4~;ü-\9euÏ||,ýs\90oÄöÇ\12\r\8f­îÂãç~\82\91\93º\ e\16¾é÷£ã¼?y\18á/£\1f\91\!p'\14z\8bpöþ|ÿû¨Ói\rÐoýHé³ö-a&\89D\8b\15û\a\1f\99þÛy\97\1fúO\ 311ò²È×\1a\87¿\88þPîèü;éñ3G÷¯¹ü¸\13\18EN.\8e\12ìèå¤Ï²\89Ùg2/vÎÈÈ Cª\10;ÅóÙ=°\8d\rôØ\87$ÑÍô\v\89\16Ì/yq\12\8cE{ü\90­\8f¤\114kþ\áìxÿ¡\14åR3\98\97³gf?Ë\16&o\91ãûîs\86jï¿b°Ù±\1f\ 6\94eÂûhJ\9e\99\aæz\8cø\94å\a!&&\8bæ}·F\90¯û\17ÅDëøPðE\9eD\84\85A\ fÞ0\14ÜÅã'Ú{g4þ=\92\15\8eåß\8e\1fJU©{!Zú\90\80QMê\f\91\88d³¥(U<ý:\91á<\9c\1c\1fu\7f.ßðLª\ 4\ 3¼+>%c\81Nü¹JÀ«F\0\19<¹A\9dz4nKùøâ;f5øò§4óÄütò·Å\9fHñ3Qý-µZ\81\ 3\9eiß]suÿã]þªÀ×\8b\9f\8dþoî÷5óåñótóö]bæ\13Ó}*½\93\91{\9e~Ì_  Å\9f\87÷Bÿ\87I\1c}\8eü_Bá3L\ 2\ 3³\1f,{Ó\9dhøÒ\8dßQ5\7fU\vW\85b¨[\95x£0Z\92}\98ýù4~\1d9½oýJ\ 3Ê2éVîèv¾\97¿=\9f/\96\87fâ¬\8e\ fÊD<\8a>\ 5T\ 34çª0÷øKO]ÿ\17õq|Z$\84\97\\12\8bÀñDZP\ 4é\bG#ü\84{3Ï\95\91³ú\89U&B\92\9cÓïÙ\ 2s      Æ¢çÓy­\9c\7f?'±Ä\1cG¦G\1fBð³\1aÏ\13Ç·OTèàíX"ÄÈ\ efqÃõ\84àá4Tøfn\ 6¥H-.h\92
+
\9c \vÓ(u\91\84æ@©F\870´ýPáë»4c\8f:w7yö\89z0ÏA½2¼7ÀÞ+\97bÑ6g5%Ü W-xüùÛ\8bn\ 4,L\96+òµæÀ\8aZÜSׯÜd?AiJ\14A\95é\8b\99×\85P}x$\14SOT$[þ\88yüÚ¸`T\9dO¡TL±<\13»ºÇ\ 6'NVî£\18i1ñ1\ró?óî\88\1f\8eï'ùÛïÇ\ 3\0Q\8d¨\0¦¥èä\92\12\ ecìsþæó;\0¯½\1eòL¤ö-YËPñ3Pa\80ÚÉ·d\ 5\8f\15Å\84\ 3\82êëmRÈ}\9d<"\ 3ÿQlÓß\ fù;\7fgfìw\98¿¹}\1e¦\7f\86±clÑ´\85\0°hÏ'çÄ1?\bNûÂq\99\fêlûÓM¸¥ç ¬%:cM\12íE)xòû ­4¦§ ÉÁ9û\ 5äõ\1aKÏÓ\1f\91£³Ù'XìÎ~\8a'\17Ãç|ãã¼ ?åò\1f¹ãJ!\ e]>îÁ
\ eyòiñ\9eo,nií)î\fv\f\1aF9±\939\94fK¯\9f\89æñéUa̲ÕÖ>q>\7fÎ\11Õà¨\ 4?º<Q%»yé·óæ    \a¿=f¡­\95Ó·\91_<~\ 3°è{¶ä\17«Á¯\82òâ\ fü\16âêÒ;òÛ
+\ 2      \8e\ 4l*@¿Ó\eøí§(u\91;O%,*y¨\93\1e\ eÆ2*\9a\88Wß8\1ft\8b25\12I\888\f\ 5h@X¤ab\88\88¨\10ß\90Úd`xT\18\vzÛÌ"\95d3ÒÚ±Ì\9b\10úl\ 6üþ|ª{û|~u\f\9dîNáÇs^ÏJ\8c\96S¸x|­c\81ÄϧÀ¥\ 2å-¯q\16°\18'Êzj×\9d\bÓ4¨X(_Ya\16\10*w\7fÎ*|2°Cê§ý)É\v\96\1c\84y\99\ 6\9b±¸\99\12<\ 2é7\ 5¢üÔð'\12\v4/6\82á \16\9a~T\9a¨,3\v?@X¬\98%\8d\15sBa\87n̨³%S±lèÆ,3På\98yÔð\8e\8cO\ 5a¤F§¤Òp\9f³\ 6¤\b\9e\84ô1\8bõE\9b-ã¬J¼C\10¥îÀ     +U\91\15\12½\86\14@/¤\18Õ\8f4/èùº¬^)/\98å\8fÒÐ4\19S'^{GRïÂFb\88§D\1e\18Öd\84;Ä\)¿á\ 1¡aH#\r¼\18]¿pþö¾:\16r\81Þ-xú\9f\94~ÁàòcX+¿jÂÇE3#øÂqXÅ\88^;\93VVûØ\ 1x.ÅAîaÿî«Ð\7f\95:\83ßCi)Ó¹\13©yÝ>\9cÑ»\1d\91\a\1cÎ@,f\fh0¡Å¯XèTrýêg¡\17m\8d3D[Ä~î>\10ä
\8b\9b§bµúËë\aÄ¿÷ò7ãâU®ñ&¶Á\83\1dæF¤\11K\ 4Ç\ e\99Kbø~\1cû\10&æ§\89½Ð3\ 4¡÷ÓR'z\9bà\99(\15ÓÅ\8d$ßþ*öÂ¥\8aæ[!Oé|Ái\8c\91½°\97ô\95.tÓ;\11\10\9a¢Ø\96\e
+\93OÉ=[\ 2ðHÃk±Iââæ%\8fè\ 2O   ¼õÔÓ6Á\89\8bÐ\ 4yJ[\ 6'.B\13ÙëS%4\86\86&\85Ì\86Ø÷m E(éa\¬Èspç\1fÈþÑã9\8fÝX\8dO\1a¨ÿ»"\v\1cH+ñTå¥Xå\99\0 \bµùëÜs\98¿ö¿Î±;L\9c\97\82OúX\ 4\8b}\96©Æ\8bg?lKc\eD\16\88q2ûóã
+{?¬Ýæ¯îª î \9d\92pj\84Ø\ 5\15\12\f\13V\15 Ì\ f\99¯gìý{ü\9aÿ¿LhïªX\1eø{ ^×Ç(©Ñ\81ߢóòt¯Íq\91#úÒ\b¶f\13¤\1c\a$\vSÎ7\86¹\b¼ýõKvnN¯¨Ü\9d\8f\93#ÕÛp\ f\985 W\ 4ýÔÓ`¿ðýâË\0ÁïïÊì¿Æa¸qÐ}~p\11\81@¤þI«
+\92A\81Á\e? N|Ú\ 3\89Lw4$Ð4\9d\ebd\86\17\8eK/q\15
++¼½\86÷\85Òx\ 1ZIù¹QZ\92¬èAâ9Ívjçüu=W§BÍÑ¥Lè¢éÏ__6îQ¬­<xøIÊ<\9e\1c\12ì`Ñ®\13µi|X\9e]õD\13fY+M¸w\8dÙãWERÈ\89â÷\1d\9d¹x8Å\b\8e\88Ó£\ f\14Ý\15\88O®p¦\83}5å+<½à      2z7ó\19æàù-Ï\1dUcê\83[ú7ÞÏãX¬\14Í\89\15!\1c\8aÒ`®\1e ¾\89vÇ°:Ý\bÂ{È÷\8dF\9aÌ\1d·ßƹ\87ëÈaæ;ÒèÃâÇý`9×Yç¥\ 4F%V<\17Ã\1f\92\8c-K\8c¯\94\95zªúk\0æN{)&\7f\88W@\94\87Y¥¿I-é謽:B\90öB(8ù,ôg}\ 6e\15bÏà?\94~\v&\88.¢|\1dØk\1f¬Ü\8aö&Ér¡:Ä]2dÚw     º\1fjÖçùj\93ìJÓxR½ì\16»#.ª\b_p\ fâôS¢X¨ìK\8bQlâËÆ¥D\80\94={8Iez\1f\87=\99O\95z\82\1fÆoH\0Ú\18c·Ãã\970^Þ¥_Kïoþg þË\97ûåß\82ê\8aõ­Ê\1dX]æÀ\ f\9càÎÐ\9aóUj'\9eúF\1f\ 6[ìg\1c»Ëëuí\v<%öábÚ0®ÝROö8Yê\95Þ®Úïà\9b\1c!\83ó\19*u"ÄK\9e\ er×`þ\1eÒ\92/ £Oîç\8e\ e\8bg£þ3\9fº\7f\8cç¯Ãõ¯üÍmD\0\97_Y~@vE½«"-('\89âÙs-\8c\9c\8d\eÜ\ 5¬óqÃ\8aøôíáACxß?l\16?\81_ü0}\10-\9e\aCAìܨº\8f=\89/ÿ\0d\82Û;ú<h|[ ¥a\11½\99\95"aæS¿¼¡¹\9a¾óo¹û\83ù\Å\\83)þò1ó\9a\0\11\1f\9fº\8dÇ\f\8f\ 2>âì¹|,\83P×W\9c¡½øùüÌô\ 3×\17àÒEóB±Y [\rÍÐ/\15ñø\8f^'Ï/N\9dîÃìeçèÊÔå¨÷\16\1aà<Zæòë¨_\8aN\9e\8c)2ßH  ¯åÑ<\1c@ý~\14\v³$Eû3XK.¾%Gæ\94y,\17.\v÷§&Ù\90w/Dß3¸Í¿70ɹ_\9dw(\83"âÀÞ\87©Ç\8f7@,æ \ eúYe\98\13á·r\\1cÖR\9a\13,OâÛ¾¯øÙø\98\14ú{\a)\10lú*?Î\9eM¬d\bÍ\v\12º«IgÊ]|&ÈüU¦TÍ\1dñS½ç-Sv\1cÌ\891n\81lr.Ͼv/\8b\9fÜG¢ôÔ\9b\1f\9aøÉý\88\81[X÷ß\9aùÉ÷U\188f\14\16Õ]Â\7f¾hÄ\ 3Q§g:)É\9e0I+\ 1á\ 6ùÒm\9eån°Ûø^ü¼\9eÁ\8a,\99
+\99ÚʾPÈ¿~       \85Ü×Måà\84<Ës\89AÞºK>ü\93»¿=k  ÇŹ\8e\1a°Äû{¹ÌCï+\97\9e\8aï8/¹\8c¥D\85Âx®@\8aêßnG*¹yF(ÇåáÅ(\92\15X_~\9f\8e\9aêD%ÁP\96f(M\7f\rÒñs\98»ÿ¼]è`g\99óE1~Ó\8då2\97?\ 3\9ds\ e?n\99=a\\90·^TuýÍS5\98ý\11¬~\ 6ö\a\86\89ïÞÛ\87Âê\1e­gGåjQ¬¼ÍfÅh[(\1d\87\ eüw \8bÍ_~t\95M-ËËõ\1e,_\83'ä)\1d\91A°s´\1f\µÛDñóV\84`\8fø\1cé4ðlöúQö\v¾jNÜ\7f\9b\17?'bÜB`9X5¿BhoçQ(|\9e&5õ\91ã\17ä\1c°(\11+¹XÂÁãO\9f R\83ïb9ëï\81\8bÁ1R\ e¶sÿ4SÁ\1e  Á³î\ 3
+._\90\8dx,ö\8eßP¨°\88h\ 1 ²Ë\93\f\a\94\0^Qù'\7fEòI\b³O\a¹ãìt_¯\8b\8a\e\ 4¿u\13:.\12³è\13\93½£KùT¬z\8d\f\ 5¹\8c\ 5\84¾   Ô­å\9a´'ÔÆ¥Nj\f\98\90\ 5³\ 2y¶q!À\1c4ëùIþ3h\8d\ 58_MÛ\81\b\82÷D|e."IXIo\ f}®4Õ öҼؠ  ¾\9a|'æ Ä·ã<A\9d
+× T\1fD©Ó\14SÅ\eú«t\8eN\90CÇH\82Tnæ\86xãPö."'çOç:îÀªóÊ\ fÙÜ\ 1\ e\8dL\ 6m.°á\ 5²\177\99Ø籨\8eT\9f\82¸¨\84\b~8\9c~\98\ 2xMÆtËv»Ô&\8e\ 3z\ f^\86ÈH\ fòÃV\ fíJì£Í\1a"\7f3©Í\8bÑ«ß®â)\r\16*Õúõ\ 5c9,uö\98W\88d|¢PJßTÌÎÆoå¦\86\ 3å£ÏÑá!\84í?\8cæ\vYx\0\17`±Kq<Ù\80ÅÁ§8z(E®\ 2\11`à[Ì©_ö\ 2Ô°z\0\98\85¸\15f\8f_Åý\90;\1e
+!½\ fn\ 6Ö°×;¤Â¿\85Ë\12÷\81\17\94HvöÄiÆ\11¬%\1aÒIð:ÔD\11Êe¢¿\9f9\96\1d\93ð>L0`#\99a\8e}ü*åÓåÙ\1cm}Ç\84àyì¾@Þ\8dfG½Ìá\bÆ|\13ÒcÎþ\8e°\17\89lòi§g´\96\8b|º=ýÒ-     Øà>]·*àëM\88£ÏEO4\9bÞI¸\98н¡n\fc¡ñø\99_\92k\80¿Ý\10\84Ü\17\bycqÛ]-¿\f\ ez1\1fP\1cp\ 1âó@\81?r|\91oDs0/_á±\1e\v\16ÎRg±÷\9eOÇâ{È!dLâ\ 5ËѸÇÓ\83n\97¹¿{½O³§\85Jáû¹»\87"¬½Üñuò­X­\1eï©\9bÅæ×À\1f\13Z\87¥ÜÑë\1d¸\9dìu\84 ¢¿\1d°òO\ 3Xs?§ù\9baêÁ`\84[\8f¹Yèq¯X=O\82ØïóSãª")\88\1a\f0\88Uà'c#Åd\7fÃçÅN¡5(\86û\89Î\92ÍRÍ\95j©T#õb8\fP\7fbo?öE\14p\85sGoÄ\aóKÌú\80å'\17\12\82Tý0r\92/uAÏoó\ fÅ/úu\0Ó4Ïé\92\92R\97«Àk)Æ°}0ká=)\ 3¢\18«R¦ÛíÜf.ßÚ\váãuñ\8dÎ*tÏ\ 2Èf½\80\8bÕÜç\aÅÀOé©Ó~²\808\ 1öWbBþÆ'Êg(\f`\91e¿\ 5\93Y>Ç6R{
+¾%~\ eâ~¤ã]û±wÃÿüd+åÛw\b{\95@\19sù±Ôú\98F`Â&\19öî-ÒP\12\9a\98Y\ f$?\8c\91·å§ôí\1d2p\1f\b\1e`\81H4ü+\1c\v\9d\993çÕÉ\99É\83¼\ eÎ\910\1c3wõn\ 2tö.j\8cÝa\r\88     û\8cx]9EÑkàô\10¥\83¿ÁëeZ ë×\17Å×\9f8oê\9e¾\82åZV$öé©(¤³OÖ°\99ê7\7f\9d?`xz6fXövÚµÌZ3ª\ e½\16?ùù\ 2çmõ³\7fý²/\ 4«|:\7fU\r\ 35}\10\9fÀÜ\84OÓD\98\97O^\f\98\16k=\9c·jI(¶\9f(;\10íÜQõ
+¼'"úX<÷}\12\86ôù58*\17B\0Å\95\9c¾:+¥\94ñë³ö\99þ[ô+\7f[\9c\85\10¬b#\14½\86\8d\989¡À'\93R¤"%PQÛÛg     °\b¹\1aÙ5.[¥ãè¢Ø\16\82·¾ ûпmbe\80\15"ÖÔÈ+ûA\7f±=`\1eâ§d>)v¥±\10T'·(~í¿\1cH *Å÷s°c\9dT\87æ\99\'®\1f+ÛcØÑÛÁHZPª\ f\ 5\1e,\91_L_=ùBÄç{ì"qñò\9bBFø\9c\9eÑ\13p&_ÃQ æ:\ 6\ 3ÿX\80°\7fEüd*ù\f?8\98\97Hé"Â\81çÍÿ\80VN\17°\10\84îÿ;«Ö[Q¨ÒÊ{\85\8bKfÞ"þð\1c>÷¨UÛH'ãÐ\97\15&R7ó\17\83ô\10t\9f´aYåTðR/­\f@ÿ\91'SÁÝ´5V
+Q\8f\96ë¹´«\a\8cßÊG\87þ¤oè£Â8å\99\a\1f\ 6Ô·X\1eé\8bdS\1fläô¾\9d \12\91\8bpäô{\9eD¿Qôñu&©>¸V\7fÃ\ f\8e\92§·s\1e\1c\94ò ²\7fsÒB\91EV}
+&ó\86ùö\85\92\95\13_,\11¸ñø}\91ìàØ\17º|â|Ñï><úø\8cû"\8b\17½x,úbÄ\ 5E$N\9aA\8c>å+\84®é\195\83u1U\1cЧW\1fÙ$\9fN¦ÁÇ\1e=á´\1a>S¦=%*ï½\82Ç/\8aÙ\93v>:½<ÏU¹Y6]9~\88\v\93'ú¾$¾<\11Å'¡y+\9cäO:$80c\19Kòà:R\88\85o\0_\8d±\e®4 x²Æ\1d\0Ç\96:Õf¢x4»\ 5$Ñ3"A7¤ah\94ÍÒÂü\9ez\9b\f\ e\89î!\891_i`ÅWbv\f°Ó\8bH¶ì;Ä\ 3ÇóR\1cÄ\13³t\99Nÿ\1eýÀ\9få!¼Ý,\1a\91¾\88¯\17/×ÖHËì[*sö\16·Dúvp  î¾      ­\86\94éWË~k¤Ç¾ 8#ý¢5Ò:ùBïQ\99°\86Ôã×ÐÎ\ 2Ñ\8b\98\rÒÔw°\15h\96¬\91ÒÍGB Â\17\96\84.8I\aà
+^Z\8d\95\10n\1f\8a6H\99}\7f½\91ÏÛ!ý ÊûOw\1aR|\ 4VE[\8eå\ 2gíø£%\83_~Z\8c\8c´\1e\b\98Ø\9b<\9a\ f»\18)Èb»d\9cÓ&Hò+u~\89Ð\86\96g5þL\9f\\14"\80\94\9e,\89ÒÛ±`\8b45¼Ú\9bkH5I\96жķCÿ½\rR¡Ã\8c\ 3\12é,ÿ\9a´CZ\ 1\8eM\9e\9b\13ë±\1eï\ 5g\ 1öP´B*.>H\7f(xúüj\85\94\10¸RVC
+X\8c³zи\17\8f¬\91ÒÍWBx­ÜX\8etO\98\1dù\a\89û\86\15R\bÂËÑÅ¥ÍX\99ý@ï+we3R_B\9c\a\bix\89½×Bø\94Íù.\0);õøÍc­±|SFÚ\8c\ 5MH\99Fmp/!-½\f\ 4ÃH\9frDí¹\94²BêñÏö*¿3v°\7fÍ`´f¤\17a±o\8b47x/äm\90>\87\88F¦7ÃH\91\8c\99\18\\8d½T»½èÜ\12iãýôÈ\16éåKå\9c·B\8a,?ý\ î\ 3\8fiK\ 6ïÕö\17\8d¯v7m\89ô¾FNl\91Þ\95\13å!Fêñ[\8cµJÜO\939\e¤àw¼=|ä-\91>\9c¶ÃVHÁ&#´ïïùù\83\r\83_hâõê6d\8dôòêëç±x\1c¶Dú:\8f_c¤h}Y\1eë¨\1e9|´C*\10ï׿ÇÖH¯r!ñ9?+\98\90¢D\ fB[ú\8916\fNW|\ f\97DMBÚÚ\9b\97\8dJs*.\1e\9ei\844²¤4WûG¡·Å\b\1c>@{"\9aÇú\11¾9\90\91\ e¸°i¥9$..S\18)\15È\ 6+F¤qqöu¾\8f\90Æ4¤\80E6\10µ¸ïe\9f\11\0in¶d
+_&\9c\84ô4X\8a\9aØëË\vW\ 1        éë<sn`oè.rT9\ 1\17\19¡M,\9bÂ;2ÆÖ\ e\7f\0©°0#\15û§!\19iæ:n\1c\8e÷\1f\1ec¤ÉÓ»Z\r#EX¤±î?ÏRíç:BJ,1ø\92;Ü{\9c_\9f\ 1RjÉ:\8bùöø.âO\1e[?åÓ`\93\9b\97sËç\8b\96ï\988{\89Ìm\9e\ 6²dkVß·z
+3 D}~þ \88\9eZY\98J\e\ 2\80\97Äf¯ò=U\962«§c\91½z­¤LOÕÙ¯Ìçi2ûÄZ¿}vèË]=\94®m\9eÎ\8fªgçû3ë§UâÕã¯E"©\85Ísæý2\97]\ 4l\9eV?ë\19ö>fù4}ùD*+)\15µàX\8d8Pç2¶ü\949¼k=\97²6O\8f\83÷\85ã\87\9cé©Ê±\1a\1f~8\10û¼ÍÛ\95è{\81¹}±~z\91\17~NBÉ\90õÓË\eX\91\7ffï¥\88ÍóÇ\9fQr\96 l\9e\8e~'±A/mý´ùZgnZ1é]\v\8e½\86\9fUé^~ûý÷^±\87\16O[\8fÔÉ^\94\15ì8Ö{¼\14æ{W=ë·?\89×ïÀÏÙ\9eåSÿÃu÷>è»<µz*\8a'ï×\1e\7f2w\1d\f¢çñåç\14\7fvuÍ\8fÐÓ%#$æßG3ßk hùtñ\91   \86ýǾ\ füt\89cðü$\14Î=\84[ÚÛÙièxªF|Sl¤²\åà\a[/¢òQ/¨¡\19k\15\9aé#\v_¸zDú¢Å\9b\a_ôþ\râÊ÷î­/ø\1cZ ßê(þ,øbÕwð\85\1e\a¬ôZöd2\0j\1a9\8c\9c¸H\8f\ 3\10\8e>.p°\83£¤Ïc\15í~¢\7fÒ\ e\81\87·W\82p'a´\9bâ\1e\158©Çä`ç`¢_\82³ûIdþÏGR°Ó>¸\19è×}=Z:ôb\8ftOx\8bÙ"%\ 4\9e¹²AÊìC\944N\1e½kh\rH\9bo\ eH˾\94=ÒrYlªH\93ȻСMWö\7f\99Å\8b\82´<42øU\8f\94n\1cèÙ{\9d»Ñ!í\1e\1eîkH\ 1ËÑðîRCkb0\92¶©5RºÙ´Gº'|\11\ 6­4¢Å±\83\rR\88&!vhÛ!mÙ"EÞx\99ÈØ\8e\15{$¶H\91?rgÇÞ¨\86Tò.\8cc=;4Í*\19\ 5_\ 3£Ç¿É\13q¹èºêwµß3é¾MϽ«\13\9f\8b~ââ}à×Y\v4fÕZ~\9c\9f\9aT\17Þ\8e\ 6#üdv)I?üV@þß\19f\8cÂYUãËW7Àã\8b¨üã\94Ð¥op~\bÅ\95tôF¯OõC0\8f\81ç\82Lwë\86\aB3\93ì4ؽ5g\8d\0=\9føî\15ýèǾ\8a lJsÉÙ\1e ç¹H\86ù\1f\ 1uJJ 4³\97Í\96üº\1f`\195\7f[Ndé:×\ f\7f\95.òpÕÜ\ 5\90L\9c§ü~ü\ 3       \83\90)»RGP\8cdÛÉ\8a\8e\81\97ïêðç!Z¹\16a\8c\ fë\8b=Q3\13Q&\92Ð^`\14ÿ\90ø)åk0\14MT 3Äbá|m±\9aéøÇ\8d>&·\18ß)Y­Y\8dÏã×\8f\10ÿh=\16­fP\9b¿äéýíŪù\8b^Èò\82â!$cË#\ 4j\8fÂÎÌr?\7fåÆ\18Ʋ\16³\1c\80ÝÛ\v»ÇïBÜ5f\11\9f\81ßG·\92¥É\95Ê1½dMÈÖA ²\16çíø\9eÐ$ykηgÎ|÷81Ëdz\9aѱÙô\94^
+S=\ 2uôFÓãñ¯\9c\8d·\12Yz\9d\95U\10Ik\ 6\96«\87²Sf©\95¥\97«¹#=ضGÑ\8fg}Òu\89w%\14AW­Ì¶½V\86l\87\96<8ªÖÖ\1a\9aq\15\93Xý²¿\9aÕ\87\92\ eÙ\11BôZïwÚ¨ô2f\18\10\8fÊÚ\8c6£Sã2±b@6\16¦ôR\8c\e,\8cª\86\ 69'zõø¡ÑW×OS¶üò\8bÙ¢æ\937cÌg&ød·J_«B\13ÕIrÄ\ 1X!þì\ 2\985(Ð:C\ e6Û¦&f½kíÕ~]è\9d³ÖaIn    j'\9b¹\8c\96¢Ò\ fy®È±µX´\93{VÓ\89<%ã\84¢\1f²\15ÄIl\vÙhí]\90¶²\91ø\9eDNLtaIÎüÚPFýRü\13wn=È(\1f´ðÂÌSbØKÊ\ eÒSÓ\94ÀÛw¢£­ukzÊÈ\1f«\a\ 4\9b\85i\95Ïh\90ÅnÙhUuË\92Ç¿æü\ eÒ\8bõ½'kÝ\aâÛ>gf¡ñ%V\92Äí9¬\95\1a=.\:À÷½oGRNÖ}·3hëÒÉFö}\11pôù\r3¸Ê¥s?\7f¢ºT%\15\ 6X7Ð*9Óåñ»\ 5¶*îp¢Ë°'\8e\81ÙÉüú\83Ô¼º\1dpÌhi·ä\98Ñ¢­Í19³%\v\1aÅ?6cÆÀµ\82T¥´¾w¬Ñ\8aóü\88\9eyv\85åÐ9´Övì«bë^{üK\ e¶µ¯P1Gâ[håW\85z]ðÕ5\ 2ei7wy&ç§~\8d7r$¾>wÜ\87\eÙ·\âN¹>r\9b\19Kv¦\19\ 5;}q1MN!\9e\89\10ÉS²$e\95\15pC\88Ù·Ü\88'«Ô^!D\89\91Á\93
+Y.Qüã;½^L.miª)nÃ\19\92\8f\fÞ$¿Ñ§¶\83Åùz\bl\92\1a\ eoy\róa£ \10\86íÙ\92äñ¯EÔ\86\ 6@\951\9dÊý\9cíÈ\0Àø\ e×\8d÷mÇw\9a¹¾pf¹Ç%Ó\8bsÍWp\95Ï0xòú\\1fõ:û&w#T \90M]ºAZ_\9cs\9dv1ùÏ\19ñIî5]ȧgõ\f\16çnUÜ6ì\91£$ĬÔ\16Ì\9aêMÆÇÉܤàÈ\8e\91ç\v\17Q÷êìÒ¹YÁí2$N¹\ 4ð\93\ f\9c©±õß\972$É\83\fJ\14¬
+\8a]¤dÏ5ÿ]\91äõÓ$\0"è6½\81,¿õ"t¾´,o\90Þ\18\9d\13½ÙàÁã·ÉI¸\1dÐùÂE\8eγ"K\97<à~i·\8c±e\vÆò.®\93\ 3±Ë×\0wìò5FIÖÙMC¦ÁèJ££É\84Ñ\95®\9a]i#ó=~\17δ<\a\99ëý\ry§øc\12=ö{R«<a\ 3ï¦Uã2\984Æbk-\84\99ë\1dèKÕ¼\ 6n ç\99k\1f~*íWn\9a\8dN\9eÞù\93.\ 6äq\96ôªiÑÛÈ\0\80Å´ÞmÆ\18çd¡gy©³ñe\11wØõÖ!Õ\97\95ü1ãR\17
+F\8cç7`T\8f\rg½sÖ:£$\83\11Þz\7f\ 2%ßc¦1/í\8aºÍW!`     7ú"É\98\15\ 1#¶µÉ\ 5lÇr\8b-e\15Ï\9aÃ\92§Ûã[\rÇ\18H®A\8d¶'\8eál­\81\18Ê\8b9g¯³ÉëÀYsÝÓÅû\16À\8c;\84k-¢\86ýA¼"7Ææ\90\12µíj\87\ 1ï#\9b\16\9c\8d\13/\88V»M\1fû=>;'âñÖ%+uî§Ço\15\9f«\16-laÑîÖ·hv\19\12Ð\8d\1dX´ÙÀ¼[íÞ\83·\0f¿é£¬\95.w\90\100j{O©\0!Bco[Ý¿³²h\eèþÝú\16Í*w\81áloÑîv³÷\8aá¼Ûn\9då\88Þü.\81ÅÇc\9bÙº2LØ\11a\eA\e\ e­}\14Õ·\94I      @\0\9c\r\9b\ eu¹=øàâÌÕã½m í~K\1e¦ÓÊȪ\19øõÌ,\0[ÇÈbÝ·5³Íéú§\1fÌÊü\19ø¥=þ­\1d\14tä âά\84cväÖ¢\ 6\7fäH\82³õ\11\b\fÅÒ\13\Þy_ Ç^sl]rËÝ\ 4\flͬ\9fÓZ\18\95s\17ÆÕða\eÿÞ ¤/¿x-Ü.âC\11Ö\8a\ 5Lµc«WC\0æ&|´]\v\r\91\ 5\0Û:çR\80\19ê\1dl¿\8a\9fÒ²\Å\1e¶ñï\rPäµp«UìÁÊ¿·\82âñ¯\84ãj5t^\v\15\vÓÚ» ¶Y\rMk!:\12\1eÕÖB\19ËÒÙ\97\8d\8e#¡Ñ?Z­\85ÚZ©?<¥\1dðõ\ 5\80®7ÛpÔÀJ¬\95Î\19\v\14,¬ð(]xº\8aM\ 6\14Ò\85§\v R«²
+\8e¶Öȱ±ßíLJÖÒ6"zt©çN»§ 
+(?6\e<ØXùÕÇéÌ$Y+©$É.ÔË´0Å-\96¥¦í²d\9bO¶]\98Ú3\87 Íæä\9a}¼ß4\9f¥·c¥\vå\82H\9c\7f\1c8ÇFn\8f¼\15\0Ô¯yiÑÍËZ»7\bØܵ\10¯ä\98\9b|²ÝÎ\80q&\91$\e\8eß:\ 2³q1
\84Ãá[\90\btBU\96\89\84\v¢\9cÒÂ\ eÊ¥®Èr\ eHs\18¤Cá\16øôßf   Þ\1c°¾è÷O\ 3UÆÕ|±tìݪ\82ÎãßM\r\9ds\ 5ÝRÅÐ\865tÎ\15tRôº}\r\9ds\ 5\9dSµà:5tÎ\15tÆjÁÍkè\9c+è<þÝÔÐ9WÐ\99ª\ 57®¡s® óøwSCç\A\87Owì \86ι\1f®~ÚA\r\9ds\ 5\9d>\v·M\r\9ds\ 5\9dì\8f­®¡3\1eH¶¯0\9bZyÛ\8egàí+\81r¿k\91d\97é­\1f\8a«\ en\9f´\83.ê¥t^ß\96'kë\87+N\84ºÏô\ 2\9fêë\9c%·ßã«\aìO\9a­Å§å*\9bå\93fa\97Å`\11\17I\14ã\1e\9f\ 3°èÖãÃ\1eìêÊ9÷ã\8b¯Ò\17×L7mæ¬&ɾòqE\1aÌ\81¤å\929\94\1d]³hÎ\9d­iFujm8Û³^\ 1Õ\1aGA$Ý·;\fòVÚÍÞ\9ctvÔ²\98h½¡9ºîê©\9b\95ÅnÆ\93\90ë%\ 6\95õeÛ\8c±Rìf½mc\8e\92V\16»mt\14Äh-\811\ eûºúÐte\18\82@©Õ\fÒº¿&0ã\11,\88\11ã\8e\8d`\8aÉ-*\1f]Ú¬ÖÞ£ó\11\a]Ðë1|ÃpiB\ 5Û"ÓµJLå:>·%\8c«c{Áj/[ç\8f\99Sg«\13\87\91å4I·lU@´Ù\8eÕ m_ØoU\ 5\86Ï*ØÖ\81\9dÝ\89uêø\9c?]àú\9c]·l{ÂX\7fæJKZÙ\97¦eç§ç6$9¸ÒV\19x Êþ\f¼:u®æ\8f;Xu\ 6~\8d:¾±ó·\ 6öרã[Y\13c/\f\86\1a^\19Xc¼\eÉÒv>ì+SÖ\ 1f\U¶äØË\8a¨e-\8eÙo\85¬=HÙ¢mÊ1câ\97\15\97\ e:ͳ3\93?¶a\0ñUYYÀ(«ºmÞÒ¡^Îl-ì\0h\ 6Ƕòñ\96\94~¬Ðîùé\81\8b Æã&Ø[Jí:\ 4{6ß\88 øæÞá6 P\19\96eÔ¢\8f\99¶á\89¾TnÃ\19\19±\8fyìKÜVëþjvØo1\9aÄÞ\96\19ºï*¬b\87\93çRqþä\89úµ\13w~$õ:kÇ\8d~äÙªªX©bȱÖG©ô\19¯\93¶X
+\95\14Oɱ@ÎÂêØWDYÍÀFþ\18\90äw\9b¶Pý1[>Í\83nÕÕ¹Ì.àYA\94[>\99wq\8c" Tع\12\81U\95q¶$\99ê\91Q9ÛZ\19\19\a\92\92ë­bN\15vî22«N§»(\8as Éüí\8eSò|fÊÈ$\ f¸é\8a\98ÎMFæ\94¬îm\7fJíÜEFFÝã[\116\8cηÉÈ\18ï\1a «\81í\87f\91\91ÑNw¸/C[?#cQ-Èýnz@X?WK\15©\9bÕç­\93\91±Éó#Ƭ:iï²8\axø\92dw§$r\8b¥Bó̵oõ¹q7Îò´º\8b\9aÄÓ;ÒùÄ\80\9b@CÊÂUw\93ÔÁC³:9k\\91Ý\94¡\9d\84Ö?$gZ_¦UWG\17V\96\8f\99\8e.ØU\v®ª«[ï,£å        ÕªÛózÎuuÚ\1açX)¼ú\80\b.®³ÿH\95^\92Ý\1dÕ\ 3Ìû\ 1Ó±uh\v:\9fÈ\92\16:ÏÊï{î¢\1eN:\ f³"-¾u=Ü&ß\1f[¿\1eÎú\14ô®ëá¶:¡êº\1enÅ       Õ\1dÕÃa\9b¼µ\ 6®ª\87³ü"¨íùÀMëá̧¡\94í¿ÝÖÃY|w\14\ 1Ûq=\9c\8b¯\ 4ì \1eN7/ú#};®\87³\88Ål¶uP&eÓ\r:S½\98à,@®ÏD\9a>1lï[º9\13¹tJb3Ý¿Û¾¼\1e\19!\97'!WÂq:\19ìz\87\17Á1\ 5ÏëP£úc\18ζ5ö\12\94å¸Ùú\füj\8b\98\103\9fw\96ußQ\r-¶òP  \9bs!\8eA   ÕÛ\7f¬+½vR\11u5÷lïÐ+Àîí5ÇÆ\8eÙªa饹A9©É\e\a~ï@\r\9bÓ\9d|\r\18ÃqRCwÑ+\86³\85\1aê¡\80\12nù­\e        \8eý~ºõ×´l\8fY#`ÆO̬ú\1a\9a\1eÔÒ×\1aÑ\b\1c\16j[éJ;\ 5Ôº\8aÔ\87]T¤¾üî°"\15\80í®"õåw\a\15©ä^j\17\15©­½\8b¨3\14w\15©\0g+»©«\17sõU\91\95ÔX\86OëV¤ºÿ\f´>$¶>×\87\15Äᣩë\1e2B_ÔÉ\ 4£Kj\98       ®\98\bwy-¹\14ÎÖ\8eí´\14\ eùc+Oô\18\a¹A)\9cݼì¶\14n«¼¥ëR¸õâÊMKáLß z\17ÿ-¥p\96Y\85\9d\97ÂÙåÇ\Æg°~º1\ 5¦ï';\14>\19\ fH¬÷Ex³?ÖÜÙ\aÕPÁ\99\92>vþö \8b\ 42\ 2\10ù\16>L{füÈðz\9b\vÆjôB<áj\vÇáè\ 2ªò[ÊÑ鱸(n6\91äR"tÙQ\8båAÎ\0]K·ÆY\8a³âúá\v2ÙÏçO¶ø$Ü7r\899_+\89o'ï'·Å\ 1éñ\17øÄùCq¿wÞ(\9eF\1a·'\93\8f\b\ 3¿\95ëÐ3P\10\1e_\84.\15Èî\15%\9f\10§{uùä{\8bb·ËS}-\16*ØÁ;\89J±Ûþ}³®O_\19JÀ²\99ÂsÓ®ØíѶê\fÝÌG:TØákµm\902ûèRíW»b·\15\15vÓ¤=Rt­¶-Rt©ö\97]-VÈXag*v«§(\1dRc        \18¾jZEj.vC\17d\ eí*ìè\90C\85Ý\9eÐ\8aÛ"%\84êqÝ\ 6\8fÏO_\16\8aÝÞ\9d\8aÝ\ e\18{¤åëý'\réÒ}|þ«þEË\ eé\8d\ 3{/«÷¶HÁ\8e\95Jw\82qV\ f¤\ f\16(¿É·Ù-Â'  Wý\92\ 5ÂÔOÒ\17sOâ#\9cÉ»\80\18ÉLæ%mé\8417i³#ªEI\ 1\8b\ 5µàtæ~å\91\a\vlM\85\8c{DE\17\a\9cÜ\95ö\9c\98¾ª±Í¥_Î\9f\12_:seOTaÅyRçs\1ejôº³\9bä¬î\91³ü¦ÊV7É­\99\85[úèÙÆÕ\90\13\8f\9f\1dqã¹>ÇKäV\9cëÛÑ%r¶ã³¹÷ÍåYÝ\95$­¼kÀ5Ó]\1c¥4|KmóûãÜëËdÕ÷ò­\8e»º»\80nU¦wýjºMó0ëUÓYÅ\ 1Z\16nWÕtVµtÖß\87Ù¦\9aÎ*'h¡/[VÓYÕÒmQù¸Æ6¶íIû\8d«éLl1×Vï¨\9aÎ
+ÔÊ/\84¬]Mg\15Ô»[\91ש¦³Ú§Ñ­\95;ª¦³ª¥3í\8cì \9aÎ*Ïbü.Ü.ªé¬ríZôº«j:\87ï\í°\9aΪ\96Îxvt\17ÕtV\8b6\9eý\9dVÓY\91dþ²ñöÕtVóçñ¯å\9eº¨¦³\9a?ËÓP[UÓ-\83Zy§ð\ 6Õtö¾å.«éÖâØÆÕtˠзÔv]M·)ÇÖ«¦³\8a°<;¯¦³\ 2\80k«wZMg\ 5À³ój:«Ý\92¥3ð[WÓYÕÒ\99£×í«é¬ÊÇ\8c;#»¨¦³ª¥3¯/ÛWÓY1Ãp[ÖNªé\Trí \9aΪ\96Îá>¾í\ 3ÀÓ`QÄ\ 1 é~±¯\15.\86Ë\ 21\9f\89ï\16ws¸+|rm-$ïb×÷ÕY\91äè]lt_\9d£wá\8eO+o¶5\b©Ê%s\9dø\99\vÇÂ\9d\b\14çZ\81\82mÕ³5QË$¹2\ 5®n\97[K\9e¬H\92Nw\9c¹¨§wË'«\83\14N\16Æ\81O=·Úk¨ä2FD\16{Ï£sã\92`qÌÚ!3§[Åì/º[£fÍú\9a;ãw®Ü¹äë_sgoaÎ]ì\ f»½æΡ\92Ë]!\9d«o\82;\9fO\96/ºÛr@ç\vàضÅ-.®¹s\93Q<_ÿº\1fÛ\8c¢ÃEwnj\95
+N×Ü)µoë\9c\ 3¶Í
+%Oï"ûVc^ë;WÓêÎê,N3×®\8a_W\1fQ\82¡e\82[×òL«®Îb\80|ªgG­\véV\W°Z>«òyþ\1d\14Ò­<ûëqUah\7f\88ÃmU\1ab\f³\93CdUÛÔ\9fY\92ÝÜ°\85mär=Q}d»ÐY\7f\eÊn¿\a\95«Å7¸PÁ|\1aª±»ÓP\8d]\9e\86j¸<\rµâ`s}äVùVT>\86¶Þ
+ÁPl?\aîñ¯\ag³%Ï\10%a8Ûj \86b²Ýv\95«Ova`«\vk\1dn`7\17Öª\9f(ÖiN÷ðÄy\95vÿU@\ 4¬0_Ëó÷ù\110ÊÕÉpÝÚeËÊ\9f^ØÄJ\17÷½:8\11È[\v\99OPC\9bù\92¦\15\91¸í\19껵\w\87\9bòvv\83!Z+wV±y?±-dXG÷g\ 3W\17\r­®|<
+oëN\14\96¿R¼Ñn5\86³^=§Í=VK\9f(ÞtTæ{\12×¼\88Ǥ Î\85\fkîð"ß+¼TÈ\10ø]Y\99âR\r·ºáN«¯´¾ãn#5\ºánó{«×¹áÎÁ\eÇwÜí¢ò±\18ÙÑMy+ÔÇõMy;©K²»ãníQY~Ú{©.ÉÍE\93kßp·â¾×èr\ e\rwn¿j\8e\ e©ì¤°K>®a\1f½º/¬mí½¯¨\85õ¸/¬míµ-S\ 6.\82PCåã\ e
+k_~­ªÛ×ýÎ\95\ 4gÝl\96Õ7"\0Î\ e
+k\11\14å \93s,¶\1a\8eýwáôGktß\84´-czX«¾ÝB£§Æ]Ñ\8fPÌB\r\1f·Îkéî}³ÿ°\8cÁ\rpQÄô.\9ab-ã*¶²\8ci©"Ì]Ø®\9b\17ÛÀýqëÃX:­|t\95¦vSÄô.\9ab÷ÍãJTèé¶\88I½SxÙp\ 1Gïí\róZ\8e¡T\91\1aw[ÆäÊ1,Äc&Ç\10a)ÄW¬%.\1dæ­c\88Ï]¬[ãÊ?~Û~©Áò\9bõÒ\17Úm+ÂV\84Enwl$\8e\99ïhÜ0\81\8c@Y&'7ña\96Îo¬Ü\p¼[pûë\1eÛ3¼ä¹ªz^mÑ\9a\8e_\982U=;Õ¸jÚK$\84÷\98\15>¥Ì¬W\10ÅSÒ/\95èÝ\9e²M\8f¿ø$4o\8bO%1\97¯0·ç\ 5>Þ)\14øD\15\1dãlL\95\85Ç?4\92'g\97L÷°ÝM\87ÏVõp\1e?*N{v¸èî:S×\8b\92¡\1e.rÔ¹²+ÂK}\87ýǾ\89Þ&\e\v¶ìkÿf{B7a\8b\94\10nùk+¤\1e¿t\ f\9b¾JÌ\8côÃé\9a»\18§Cj¬\12\13g\99ÈL\97»0_tG?~×\8eljÿö\82¶¥iââ\83´,Â\ 3\8ea\ 6\1f\1f\8dtwÎ\99ÊðL·ë\19GúìTù7¢\f9XsíßäöÖ\16éa­÷ݵCÚ³BªÜû\96¾¼~´e0Qº|.Û MW\f\97\17\9a\91^c¤º\15y\1f«¦L\0þM.ÂÌ,;u¿#Û~òYk\ 5÷KÍ\15D:T\97úÉËd\8d±p:\15Å\15\16Ã¥»\9f\1cóÄ9«ÕÎéØdDúº\9cé\16³Ã\89ÛócNkósѸ¯@¹È\90Ø\97$ýº%Iª\80p j­£0v\85\8eíèhÕsÑö`\951\aë"\93T\ f\98\8f ¹\98\9bY\9e\8bë\1d­r(\03\7fyróª´U'4ås°®äib\12ÎMÏö\14×9²¹\8a$£hZè\8bk¦»?¥\85óÉN¥\80öyâ5õÅá\9cVN¢gùl¬Ñ\Å'K\9b×ͨî¾LëJ.wæê­ävûÒ!\a\8b¿\e·\93¯Æ\95Ðìï¨\1aé­´\83]\1e\18ÚËöù±·Ò\ e¾Û\bsnµe½æw­7Ì,\9bÏ\8e\96\11ZT\ 3è¶^leÔ\82\80Ù\9fÒr\93\1f3\1d\1aiS\13sÕlk¯æ<f·\16¦M-v\15#»H\a»L\ 6\v¶gæ¤Ý\ 47××\19\98uaÿ±+«\94\1dæ\98Óµ2øË\10\86\ 3\ 4åÕ_]0û#¶\91x·ì°8®WH\96\9e¯¾3Åu!Ù\9bù;\aÚì®]cµÊy3dc\15\92\96ë_\ 2'=Ûï\1c¸ðÆõ$-\9dSÚ¢\14Ðhï-K9Mß¹²/\ 5Üjþ\fu\16\81\93ûØÎ\84á>nZ÷·\ 2\96pA\97±ºÖ\ 1ØÊË\7f×áØÊû}Ü\ f\92Ú%Ç\92»ä\18m\vl©\xÙ;Ôôe\93*@·5\80æ\13Ý\e\14\88¹\b=\95¯jØ\81XYvìª\ 6\10ÍþæU\80æù³«\ 1Ô\9dPÝ 
+й\86ci_lÃ*@\93¨ØF\98Ëç`ífh\9b\eõ$\8emZ\ 5h\98\12\87\1aÀ¥\1a+[vls£\9e.FÞ 
\9e$c®Ä´_)ѳ4*\93÷´ö¥|kÝȶñ¥|ÆÛ²þ]\97òYe\15v\7f)ßê\eÙvq)\9f\14#\avÃ'ÛKùðNâz7àmp)\9fU\16\97ò9Þ\8f¼³KùÜÞ_¹Å¥|\86¬\ 2"ªF¿Ø\10uuçª4ØæV¿]|\e
+Ýë·\93\9d}\ej»{ý\8cC3\176mpÚÖò^?ç¬\90E\8dÕF÷ú\99¤Òt«ß¦ß\862ßëç\9c\15²\8dÄ×¼×Ïf\86ä[ýì¾\rµî½~®$yë{ý\9cO\86è2W[Ýëç¢\92k\a÷úI\95\vé\u}Ùò^?ç¡-ß_¹Ù½~ηúmt\ 3\88Ž~Î\ 3²>¡ºþ½~\96ÓäüUó\rîõs>qfó-h#wÜ\96\1fÙ\16\1fé¼¾­îõs^èð.Ï\ eîõs¾ÕoÍ/PÙÞëç\1cêÚ\9c\82^û^?«SSÚ­~[ßÇ·\83\9aw÷÷ú9CÁ³¿\83\9c7Tt'º·º×ϹpÖ¢"u£{ý¬7T\94[ýL·3l|¯ßò18ý­~Ê9¥\8dËSä{ý\9c\8d\99Ç´vmz¯\9få>\8dêBHQÒö÷ú9\1f¸6V¤n~¯\9f¡~kéV¿¥ï\mx¯\9f3\ 3\95\eÙ¶½×ÏQ×î\90$ïâ^?çÝá5îãÛâ+\1eÆûø¶¯{°¾Õoýj\ eë{ý\9c·\88íOÛ®w¯\9f³'/Õ¼o\7f¯\9fó­~»©J[u>Ããßͽ~ÎûÍæS\ 4\9bÞëç|«ß\16÷ñ­qHÃé>¾­?¦£Þê·\93ûøVÆÒ.¾Þ°|¯ß:Wñ\99,Ìv÷ú-\15[\18nõS+\86¶¼×O\15/Ë[ý6©J[ßͱ\95±5ïõsrs^~!Þßɽ~»¨{]}¯\9f»º×mïõS¡¸¼\e}³{ýÖ;seq¯ßæÅðê­~\86û\91õ»Jk×<9Ýêçð\95³µîõ³±c²\e°´\8amx¯\9fóA§¥\9cÒ\86÷úÙ\95ÇÑ«8æ¾æ)\13\95[ßë·^\¹é½~\ 6õt«ß6§\aõ÷ú9K\84Çe\92wÕ½~Î\8e¡º\93¸å½~Î\ 5±Ú\8a¼Ý½~ηúYøc\eÝëçä\ 4I>Ì.îõsëÃlw¯\9fq&Í·ú9V×®q¯\9fóâ°¢\8aÓõ½~Î\8b\83ì)¡­ÊøÒòP£\1dH\96Ö\85å\13\1f¦ïJ%\ f¸ABZ÷u©]¼\8bi«ÎÎ\aï\8de\8b&;FGo\8c»7\87\ 6µ\ f\8eô)/l\98Õ\vAp\99\80R\ 2\95Ö'»Í'\88PE\98\1cZøb\89ÀE<Yã\ e\94\99(R³¼/ü%^'bþÌaòá"\95O1\91Ù÷Yb²h\ 5*½4\17>}>xÚó\9dÍC¾¼p\93Øk¾1\99\83ÆÃ~Ñ?\187j\81Þ÷4æñ3\8dÚï;;èÖ¾r\83\8fËïj¯Áq\97/\95ßû\ 69­~6¾¯ûûrâbqÿ^\ e\ 5ßß\v\91ÐO3õs5ªG\8e\91§Ü\ô7\82\ 1QL\1eøö&½IÂO\1c|\1f\85\9ej\9dû\b\17¹\bf\7f\ fF5¤ûþqQ\14O3u_øµ|á£ø«aä¨Cç\b\81\12Âí\83@\94÷'\97Dùêò[\14û§1qñ\9d\rÌ\ 2ÑË6\1a¸O®´Ìþ\96"'éËg4%>\öF\94îÙwqöu¾\8f´òªgi\90äyÁõ¥ÙÙ¼ø$äkÂIþ¤£]\ 1)Ý#\18à\7f\995$éÆ÷Ý\ 1øÉbnÌ\ú\1e\11«±J#]|Ä\ fÂ\87þÇúAúhÈûë\8dòyà£qvBs\87\17LD-\ e\85iz)ÅØÚá\ f\88ED\98í g1\9fØ\7f\ 5IF\ 5\9c7°´\\89F}*èÕçÃ?4äV\91;!ç[ymhÚê£çÃ)\eð\80àL(ºôÀÿÞç\12óp1ÂÑí\ 4_¢*9h»8Ï}ÞÝ^æ+ÌÇe\84K\9d\9c
+ÜÁM·ðz¾_Á#¥øf¤$©5ÞUÉ\96\1f\82è·`¤\18õ/\84`åì\8c,½e\8e=þB¿\95 Ñä\8cK\9dÁ/G$\1e\a1ê½Ò\8d\12\89Öo\f­ñ\aèDD\f½\r\væÉd\90<½Û\8b\8a\ fà`\88H¤è\10þ\138\96\9bÀ\9f\99\bþ\13\7fµéæ\17\1ar\18N\0 ö¢ø·äÁññ\87ð\1e\12\9fo¹¯ÜQÍ'\ 2ÝU=¡Qbü¡>\bë\1f\14ümõ\ 1:9¬{tGvÕGqý\83¯\93\ 1¡{\10;8ûV\1e\\86ðHÉrm¯¥¶Epg\8f_ê^~\8bwÔG1\1d\9cò4C ¶\84¼$¤.    T°4"+L\8dB\7fR\12ìö\9b_\85}\1d\91º´§$úJÇuL·¾\1c\80°\ 4Ñý0×q©S\87) 8×\ 4þl<Ù©\á?e°\9d§g\12\87)D¢)D\12\17\8dA\12\9eÞFñS*ÄdT¶ÜÆ\15,Dè MPÃ\86¿\14åBo¹#âþ@/\92J9¦\14a\9a¢WSÔ     `\13\9eÝ\80%t`ÉÄþì8rw$2Ù;ú2Ͼv\83R,ÆS¡§\86O\96äæ3\95\1fMj³|õááC\134*´è\ e\14&<Dµ©£\8a\95\13ä\13>HrN\15\9fÎHI\ 1\8a\9d«\14þ\r$¹8¼£äÖÅó\eV{ª\14j½/­\81heÓUÍ\9eLý:\ 3 ê§\95\ 1@5ï`\ 2\96\f@\8a>\86ß\8eÏs\89Ùô\14[\81â¢U¿²4\0¦ûseY<\1fI¡      \98è\90´cu\0\1ckmDétÿ\vê\1c\8d£9\bÂ\9f/3ô4\11)Ý?V\10+_ä\13Í)2\88¯ÙÄ7bÂ\9flXÒýÖ^\83Æ\90Qt\ 36Y\9e\rÐ}\l\8d|\8a'XÙïEIL{uöX2\9e\9fw\99gËä\bX\86\17YJÔ\1fè\ 1\19\r|'\90\1e\87\8cºÏÍQη\1aQI~\ 5¢Î\80Æl\ 1Û\9f ÄÕw§²åË\9e%\88T»z*Ù\83,{_)|\fò]ð\1d§¼Ü¥\9c       ¡É¹ÀQR\8d\ 5î¤%Á ËwÁ4Ìï×\95iWÍÊ\ 1Ñ;-Kñ¼\9câ^®­ÆëÁIâ\aû^øbe¢·\97Jhë'nC·Ý^Ë\vù\89\18Ñ;<\18ÀñiC\aàì%\1a×G|x½ËÞ½\87Ñ\19ã\9b9r_î\f\92\ f\85äú\1c>QçºåM*\90?¾Ò\0\98kÞ1\88\85\ 1Då°¢¹t\88'R==\9a!°¿ÑK
+\96\91,ØÃh%\82~\8b«m   µ\r&,ÚøEÞÅ<(J\82F\ 4ãO:Î+£Æ\1f{øP¾ip\82LæÅ\18?\b\ fn^@@è²ùc\aÈY\94\aD\87^ñýÈ\8a·\83>{ 8:¡&z°/»AûK\83\83y8\92ç\85\ fUUï¢ò\1aßû¹Ã\8e\ 5òG\90ѯO05\1e¿áÂdì\94\12Ç\92ìÕ@.ا\16$Bº§}å\ 2­á{
+5º\8aÿ°\8e\ 5Ïb²\8dY\0\16\ 6]_­1!Txê¾)L\88Ç\f\7fU&¼éY@}]«,xÒ³@ùæ\86²\93\98\9dg\7fW1¡u|©1!ù<$Óæ\93Ñ:¤º»£¹=åK\14NL\88kÒÿè\9b$J\92\1c\88|óÙ\9d\1cä\168\v\87E{áÓ\98à£gÏ\87*\13\1e\1dä@úµÄÅ\17;Az\94\ e\ 4Þ\9e_\ 5     Àý\ 4\ 3@ë˦ \9aSM#\9cõA\9d\173\88\97ß-\87ñ.j\0ìµÒ\11D{æv\18ò\1dÜË zó-\87ñ½0\89\94\12\17ª¡o}©4d­³õûñ\86Ú­\0hN¬\0ÀXÜ\83x\99nkaêï6FÊílÔÛ¢-\r\1e¿»aôf+g#| \ 3pZø\9d\18vF²õï-\85ª>X8\ 3\90\11ÄÄ·\9a\95\8eK\9e¸§ZKº9Ò@\10\95\8bfGß³ù¢ÊNSG-!¼V\8c÷#¿OUÎ\92z\19\93\97D\17\8ciöìç×¥\8c5¿gÛMNs`?»:\19s\ 41^l£®øntq\ 3£a a±çL\83$cNªÒnoªñ
+\80oQ\93±\rA\fÜL§ã\8aÜ\1eÏ·Ñx\0ð»ØvEn¶\7fõ\7f>íï\e\10ô~%\1f;\9bª\ 4¥úÊ\88ñûN\92\8bLôæ¹4öÌÔ\90ùM\17\ eF/Rð\1c8iJ¡0Jgã\1cNL\8eÊ\94@Q­ILUÔ¸³\12Ä¡\rÚ\ fBþh%\8c#l´Ç\89>\ e\\89Ê\7f\16âÈQ«ÄÕ×tQ§> ËÒ0\16\95ÐW¢\e\ff\94G\99°ö\0ç¸\94\a¹¨ö\80
+\9cÖ²Ê\ 3!®{ðºxË)\ fj\84\1e\8b)"Äa\8a\1es9\17Ñx§Ç\\16\ 3\1dX¢r-!Gâå+\ 2\7f\7f,\15I\1ep#\90§ò\1d\85`C4Ý\9as2\88z!\82ºDá\a\vëuý,¦Dw\8b°\1c>ÕkqÜ\ 5\10<\82`×o\bä6\93ð£\8bþ|¤ð\19\92ýpòô\16ü]<\83¡\83xHÉ\15\9dá¹\ 4,\ 5òHFÚ¼\8ac,I>\9dL\1fõÞB\9f\85oF¸â\87éý´>fÅ!\ e\18\94ÇoÚ\ 1\99\ 3Ø\9bÄú`\97êø\96ÁÞ\11\1aXæî$SÈß\1d\1e}\14¾Ùê4\7f\9b\1f? ±ç(þ9ü&§\81:\8f}5\17\96T\ 5íC\9bgdaÚ5MlZ\10ùÇÁ\8doßàüg\14}ª%$ýÖڻǩ\ 5ÄþFXú\8d\7fJVP\1a¨       Bu|&Ñ\8a¢\r-A\8c\83^%\ f\13Χ\ 3rì+I\7f}"\8bÈã ¢¨Ü\eÌå`/\9eèïÕ\ f%y\81xw¦¤Xâ\ 1èÒ\8cáÓ$8\e+%hP6\16\8d%\95\89\90\95\83\9f¬¤rªfÁ;÷\84¤whë\ 5\15ÓÄñi\19\94ä|\92ÔÐúèX\ 5_\94UPO\80©'ºq\ 6\17\85}æä-ÎQF\vþ©L2h¥\94\91\91\12Ä&]\9cµ\aüpo,\90Ñi\81(\9e\1fO\bèrEê´òM:\98$\89@ù$¬\8cï2\82\r\9c\96\9fÿï¬\87á\92¤\97#HÎ\9b¸Y\f\95Øÿê\8f½QÏ\91'\91?#É»qw"\88½Þmï_ó⤳\18õÆsoÆ\9bÈ7
+ggéT±×\99t{^ü\85\8aÔ\a«\v°%ì²ä&í¶\8d\8eÐÍb|ñ\93+\ f*û7'­â'ÑÌê\ 4\1e¯\1c¡då\ 4m)\81\8bd\aǾÐå\13ç\8b~÷áÑÇgÜ\17Yd\1a¾èÅcÑ\17#.(\88\84\9b\96\e³:MxbFODZ\ faÂ>$\89®E¦1{ÒÎG§\97ç¹*7˦+Ç\ fqaòDß\97Ä\97'\ 2\7fá\13ïÆ`˳¼}Xãlwɤ\ 1\85_\ 5\88\9da\ 4?\8f¾èý[        \88\7f\ 5c­=4¾sôãÈ\17Ë'ë0êÃ7_¨?Ìúbãë\17\13çвçw»R)\99ùuÖªõW*4ûë®Uë¯TúuÇíZµþJ\ 5XÖ^«Ö_©ÔSëk¬Uë¯T2\96U\8b
+\9aZ»EÅÅ"åño\ eÑýú\ 4\92ìr\85Úf}òø-W(m§Ýx:\ 6Þ\81¹UÔ>ãBí±Ò#ÝWÕ\1e\8cY\10Y·sdÇêH÷+¾hèôÖBí\ fÌj/I Ø;iµ,½äñ\8efXý¾bª1\95;µ~QBµ\896\18è J\r#M}\9f\v%ê"-+;ÊHö~zh\9f \12\92|\8dÏÀ/þ\13~4'Ò\ 6'\15Èù^åÝÉB\1c\7f\97ì¤\1eÅÌJô\1fçqiáëG3\11¼Ý\93<8*\9d[\1d\0\83       »¬*Û\1dò\ fôàNÞJɲ!M{±7\88t'{\12QH®Få\95ó\8e\8c[ìh&\94µ±F¨{\19¤~O\92ÿe¤m
+X\19Ãx|\927\88\1c\84\97¼~\ 5qØÊÖd\83_:Þ¨\9c\ e&\8cw\88)\19­\83Ò[Ê´5`Ü[\88é7\ 6:éªioÁç\9b\aÕäʬn\ 2\80£§p9NWòJ²ýd\12\ 4\89]hi`*È5\93\86-\10fßçç\ f\8a
+\80Ρ    \0ÂÂ't Há±Lj{z²ò¡\15\19yy\89~ö®\84½8$ Y,\9fJÛý¯ÒÖ\14\13ý«qDÛ\9dDµ½ ,\ 6\1fq\8b\94\ 3¶NÒ©\a¼ç!\1fnðøù\87pKÙ\80ÀB%çÞÍÛ\ fÈ\83ÜpûA:\8b\15Ú2÷N®H\15¬Î6\fÒS\85     ¹dÄðùï\ f\85\ 5tBc\ 1¢f Û~@>µ"ɶL8Í\fÆ
+\13Â~ãöCÌí6\14þZ \15\13NV'\1a\9cäà4S\9fk;0\1e¿\8b\8d(\93\1c Ò\8b\80ÂE¼s¤pQ/H\8e;YÈà\ 6×\ 2±\ 4\0ߪg\0\80¿©¾\ 6\béF°e\8d\aýn©\ΰÅ0¤\83ØË\0LZé\b\ 2ß_±î0,Îûm3\8c»LÂv[\ f}\97Ì\ 5\88·\1c±Öö¨    \0:O\1eø\ en£Ý(B\r9\ 3À»¥\8e ¸yx;\1a
\88r\12Ï\ 4Âål\90ÑZ$ê\82\ 6\8fß\81\8a;2¶Í>-\19}c%\91Òö\97×\1e\ 6x3n¥RÖý%\10Ó\ 2±\92\95NK^ìà\8cÄ\0f{å
+¯YKf\1fí ÿêz\82c\15\92{
+?¤Ö\ f\9dÇøÐ÷«í\85\15ÎâuO欴 ª2æÄ\98ò[ÜÍüÚÏnù\8b\8ei2¶Ñä\94§\197&C/cf\10\95\83\b«aTb\ 2¡ÈØfF\ 3\9dÄ#ÝÒ Ë\98\99\8aö\9b\7f;\8doOIÝtêVd÷ :\all»\15\99ìÄNâ[i|\87)\98ÕÕå\8a\8cOI¥È Å7É3ü¢1/\1a$#\ 3\16Ý¡Ñ|Sbà\8b±\1a\ 3ÇÔx\18â·¡\14ñ*é\9d¦|¸L\8dýäÈ0\15\ f(¿\91A\1c«È±\96îô\1a:(\96:\89*'WùÇgtØ­\10W^\14\12j ©??
+ëл\9aè\f\19\8f£©\a\11ý\83ZD:B\8ag¿ª?D
+\86´§¾\93Ð?\98\16´C²¦\10\ få«.õçcËwAõ|ì¥ñ|,\18\ 3õ èe\1cÇ        H5      )¸\ 6\1dC2}IJQgåô\ 6ýy­\87Ýþ\8a Î^Ë\81)ÈbFÝa\8c^àM.\1e\89\17\92\89ë\84|rõô\f±íZ\ 6Û¹¾Ã`Ñ]*Çû8©\83nÒñË)\9f\18\8d\84æVÇ\16*t\9a;©T\14%ÝÎoøaü+\9c¯w>kÅê\99¯a\11\86\16\8cIkÓ\aGx*T\11\8eÕä¬\19\1e\8e\91×\86x];ÑÒ½©yý W~}J\7få\e\8b\83~é©[G©ö[R\93^|o²\9aÈk¾%\15ñº¥õgSOYõ¼ñCL> z]@æá!¡\88ý\ 3Àn¿¢\80ë\81\92\ f¸ b\82eì!\89\7fW+'RÊÙÔ     \1a_âP:".¿x1\96%þª\17\96¦DM\1dayÁÇÂQxHÈÇÂ\1fJ-9s\85\82\ 5\9c\1d\r\15ðÛqöTK¦&¨bì9\82\15MÕ¦\18\96\1c¬k¨:ìQ=í©Üà\ eªgúÞ\82úa\8fÔ\19\81ê\87®å*_¥Ô\17§_Qü\167e^ñHÉÒ[÷\19\13\8ct1&)¡\94g\ 1ýóøu\1a¨\9d\v\ 6CiSÂtb\ÙoàÑj\1f\97ÇWÎEðÑQå`ð쿳\1e\90^´Ýð^\1awõ[\r\1e¿\1fZ\1a½ùb\8a:¤ÞùÞW\7f\kýé\89\1eÒ+ýCÀ?èÿT*åe9ô/áM¡ÆZÛ\13Ä]½TÈ[\e{üï\89¼8/ö;óþdÜ\12ÿx3¨éñ¢vwVôf¼Rßwè{ä\r\ 21Ä;ô\86G!´½ñ\ e\ 4¾{\bo\1eþ{ü\epç§ð£\b\7f]y\888Ť¼_\1eB&\ 6\81?Îá\97\1fhúÛK{/¼Ï¯\84·\8b^¾ñ\90\14Cx92\1dO'      Ò;\82¿I2N°\1c©5\92$KÆi\8aà¼i\96\8bÃ\1f\9c¾)     \18\8eGkbÓ\10vS\0A\83ÅÒd\9c\19F§þ\ 5o%\89t\9cÃo©]\92t:\9eÂo©\80Ô&\r\9dÖ¤\12¥ÂÒH7\8c®ãùD\\9bÂ\ fÄ,i®\ 6\0*\99\8a³t\92\ 2\84T<ͦ9Ä\87d\92\8eÃ\ 3hcã$A§\0<\ 5à9\96ÄM4\95fQ\13©4qÀx\8aõ\16àM\8a\8e³).\r³ÏÅi:M¡~L<\99\ 2 i\92\81&&  Md*\9efȤ7M\ 1ý4 E¯\ 2©I\92¡a\9cé8Å¡7\93\\9cå \17ÃÄÓi\f\8b â\1c\rÏÒ\1c\15ç\98t\1a¿H¤â$\87\1aY"N£FhJÆSÀ\1cx\15^H2À\fx!\95b\18o\9a\ 6Â(\ 6¿I¥SÐÈBc
+\b¢9Ô\8feâ)\8ea¡    xL\13)ÄG \87H\92\18\18A\91øM\86\8d\ 3oq\13I%½¸\81I\ 2\ 4ÜÂ0RK\92\ 6
+M\0´\0¡\ 4!õI¥ÓX$\80ëTZ\82\8dç\ 1M[<Ťi©)\89_\ 4Z`\82\19\14\13'\92\f©\87\95JÆY\86\18¡\89Fs¤Q\ 5-\14\99ÖS\9e\ 2V&\93\8caxt\12È¢%.P0UÐ\r\ 4\85¢\81£\0\80a)\84\ fd\ 2ä1M³è\7fè-\12\1e¡\96\14\87ÿ\ f\rÀL\98\96$f\ 4¼\0ÔÃß\1c0\9bÅo\900\ 1è\ 5`\fzN !gñ¯ =è\ 1 Pº\82\86$j\87¿P·4AâÇð&L3É »CÊÄ\90\Rn\ 2ñ¤I\18:\8c:Îby\ 5\9esHªI\10æ$C\81´²é8ä±V"\9a¸\14Í@#\rò\9a¤\116<v\80\ 5\12F\80jz\9b\1eÜH3lZk¬!ùOI\80Ì\8dé4È\94¡\11\ 6J¦HZÂ\9dJ"µXÒÅ¿°ÖJÿ\f\10i`'\90l\924Ìy\12©ªÚD\ 3\ e\02ôàq¦\f\rHY\90\$©8\9166é{qxv\96[dlòkð«±\rÐ%\91µ@\93µÔ¦ïg$~\bc\83\89\83YJ¥5´Ø
+³òË\1a)Z\eØ\ 4\ 4\ 5·QË\7f&\11ÅÒ\væ¿(Vý\8b\ 2e°n!ô\18ÓÒ°,Ú´ai#°jSG\80Æ
+¶1\95¢\rÓ§µé\aÂÉX¬Út<Áª²Ü {\v&<M\98 «m:zÍ\94Iô2\1cÖA\1dµr\v\86ÆH\9ccÐR\91´nÓ\91\96\96\19lѤ\7f\93\85Õ\85´iÓÑk¤\fQ\e^H\10\11p=Å áHäõx¹´ÔKkBË\85QÐ)Pî¤\ e\14n¢q'\8aÀdã\16I\9f\f\91²%¢0ka\rÆð\10¸\14VDD+\97\94¸\8bͪ\f\f¿*÷\19\9a\ 3¿ÃK:^\ 59Ò \j\93n¤`pÌ\7f\9dÄÞ\82u\936"3&<"ä\rèû\8dtM:|°Þ\9b)R\9a 'ú\13tÖü\12xG\ 69\87Å
+p G\81\94\94Bîd\9cB\ 4\89\e\r\17ÅJk\83¾Mß\8d4\8dY\96¯$R\7fìÉ \83\ eë\ 6\9a5\1a\89'\89\1c\v\12Ò,¥°3F\9bÛH2Åj/£FpF¸48#\86Æ\14rc8D&ø\80©\94$\v\80\ 6¯\12ÆF\ 6<\13\f¯/Q©\89\
\1c!\99M\8a¢$±\ 3g\92N\ 1 à§qiN\12Ú\14        Ä&Í\8dæ·uò\17Ã\a\98ÐÊOÒ\80\81\ 5\96Òþ®y\82\97!oXí\86ü?\86ÕºÉ\7f+ÝHÉS½\82\90wÍÄ\19\82£õï\ 1Ù°ä\13\ 4­\7fYߨ"Ö¿nÙ¨¾þ)±9Í!ÇÑ\80\8a\ 2\87\14\19_\ 3*µQ\83ª{ݲÑ\80*\85\97à\94     \15\a\9e\1eM\99\8d:¨Úë\96\8dFTà,¦\88¤\11U
+¬4\b\8b\11\95Ö¨\87ª¾nÙhD\ 5Ö\v\ 6Tà"\82L\99\8dz¨êë\96\8d:Tà\13\81YPã<0K\14\9c\19³\1c·\18\8d\1a\12Ýë\96\8dêë|\e¤2x7\1e·F½®÷Kluûè,\1e ñ*¡ÆÀÒÿÑOþË\ 3Î4xu\fþ3\ 6\83\ 6\8f\ fûÝ
+\15\f)!|\1cyÒØ1C­\86ÎHmh­#ßq\r\93·\85©\e\9c\ e.â+Ïc½3\84Ñz\85\ 1¾³)³\12Ê\8dF%\ 4[\9eJ\9aõͲQ}ݨ\84zTª¾éQ©\8d\1aTÝë\96\8d\ 6T\9aÂèQ©ú¦G¥6ê j¯[6\1a
+£C¥é\9b\ e\95Ö¨\87ª¾nÙhD¥*\8c\1e\95ªozTj£\1eªúºe£\ e\95\83\12ê1«ú¦Ç¬6jHt¯[6ª¯ï^        \91\82#\84+\95PéèB      Õ®n\94P\a×V        aY\8f³I\1a¨ hÐ19^\ 2|,\89òQJcÍÐ\ba\17ÐX3¼mÕ¦¾üi\9d¡\vÆÀ¡¢É\14\97\fy\13\8d¹Ø\1f\7fy\83<\9fït\16£\9bɼ\85úêrtØ\89`Ác5\11\v\81\1c\99¦MĪ\8d*aº·­ÚvN,¸RÒü\1a\88%@çQÒÈ@¬Ú¨\11¦½mÕ¶{b\93ñ\14\8cÄÂLB¸C\1a\89Õ\1au\84©o[µí\9eXpXÓ\1cc"\96\82\9d\e\88U\eu\84©o[µí\98X³9#!\ 2cÀ»7ÑNÄ\19\92N\9ahW\eU:uo[µ©/Û\183Z³e1Ùl\80Uc\92i:N¦QB\v~\ 5ãÁ\82ÿ\ fö-\ 6n<Ç&%c\84Í\vÊ\©d¡|,"\ 1Ì[\fB\88\14Ì6K\eú\82'ÎH\80\95¾`\89b°l&S\1c¹\ 2*o\v\15çá¨T\8aÖC\95íÛ\963\96пéM\Næ7½ÎDì\ 2\ fÑãU<KÜôZÃ\8b\16\17ôö\ 6\vù³²ÌúÛÏ\898\92\1eÉÂá\ræ»\93vï=\7fÆ¡Ý\88ÆüÏ°÷®á7\88\10é}ì¢9¥\9cçÔ´>a\8e0Þ`ÈûøàhùI"Nr´Éò+\8dFËÏàÈÖhå-Ú´\97wnù\rÄ*FÞ@¬jùUÂtVÞ¢m÷ĪvÚ@¬bä\rĪ\96_#L³ò\16mÿ\ 6b\15;­'V5òzb5˯#Lµò\16mÿ\ 6b\15;m V1ò\ 6bU˯#Lµò\16m»&ÖÞò\ehW\8c¼\81vÕò«tꬼE\9böòÿ        Ë\8f\17\1a Á\8dåWú®¶üjO\17\96_\aõÿ[þ\15\96\80¿\97\8c\91I»¿#é/\96å ôàÐÎ,H\13HTÊËJyci\97\1dí\0\12\1c\8c\9fãÔÇ$\93Ö½\8dþÒmc£¿Òd\9c`ðþ±ò*GQ*dùw´      ­ V\1ec*Õ·u4wÔü#Þ\91¦A\82H)wJB\0E¤Y­\91\84¸9\ e3\ 5\96\80eâiÚؤÛo\97\9b \86úßÓ33\ 5M\rÃÍ2\a\r\14[\80f¨!óíP\1e\1d\v4\12¡\ 4Xÿé\19\83\11\ 6Á\85\90çÛaBpGÁÍB8\1dÅwÉ\90¡Q\1f'Èr    ×¼\14ðª\ 6]]^UÕ\80ÄôÔ\90¢ÄÌ\9cÔ"ÞôâIJT\85ļ<P\ 2O-\0Ê\0\93LjqI~QªBqF~9H\ 4¨\ 5¦\UÕÕß\8d\17\0¨K.\ 5\rendstream\rendobj\r454 0 obj\r<</CreationDate(D:20090603145654-07'00')/Creator(Adobe Illustrator CS3)/ModDate(D:20090604104953-07'00')>>\rendobj\rxref\r0 459\r0000000003 65535 f\r
+0000000016 00000 n\r
+0000026058 00000 n\r
+0000000004 00000 f\r
+0000000006 00000 f\r
+0000028981 00000 n\r
+0000000007 00000 f\r
+0000000008 00000 f\r
+0000000009 00000 f\r
+0000000010 00000 f\r
+0000000011 00000 f\r
+0000000012 00000 f\r
+0000000013 00000 f\r
+0000000014 00000 f\r
+0000000018 00000 f\r
+0000026109 00000 n\r
+0000028838 00000 n\r
+0000028869 00000 n\r
+0000000019 00000 f\r
+0000000020 00000 f\r
+0000000021 00000 f\r
+0000000022 00000 f\r
+0000000026 00000 f\r
+0000026180 00000 n\r
+0000028722 00000 n\r
+0000028753 00000 n\r
+0000000027 00000 f\r
+0000000028 00000 f\r
+0000000029 00000 f\r
+0000000030 00000 f\r
+0000000031 00000 f\r
+0000000032 00000 f\r
+0000000033 00000 f\r
+0000000034 00000 f\r
+0000000035 00000 f\r
+0000000036 00000 f\r
+0000000037 00000 f\r
+0000000038 00000 f\r
+0000000039 00000 f\r
+0000000040 00000 f\r
+0000000044 00000 f\r
+0000026251 00000 n\r
+0000028606 00000 n\r
+0000028637 00000 n\r
+0000000045 00000 f\r
+0000000046 00000 f\r
+0000000047 00000 f\r
+0000000048 00000 f\r
+0000000052 00000 f\r
+0000026322 00000 n\r
+0000028490 00000 n\r
+0000028521 00000 n\r
+0000000053 00000 f\r
+0000000054 00000 f\r
+0000000055 00000 f\r
+0000000056 00000 f\r
+0000000057 00000 f\r
+0000000058 00000 f\r
+0000000059 00000 f\r
+0000000060 00000 f\r
+0000000061 00000 f\r
+0000000062 00000 f\r
+0000000063 00000 f\r
+0000000064 00000 f\r
+0000000065 00000 f\r
+0000000066 00000 f\r
+0000000070 00000 f\r
+0000026393 00000 n\r
+0000028374 00000 n\r
+0000028405 00000 n\r
+0000000071 00000 f\r
+0000000072 00000 f\r
+0000000073 00000 f\r
+0000000074 00000 f\r
+0000000078 00000 f\r
+0000026464 00000 n\r
+0000028258 00000 n\r
+0000028289 00000 n\r
+0000000079 00000 f\r
+0000000080 00000 f\r
+0000000081 00000 f\r
+0000000082 00000 f\r
+0000000083 00000 f\r
+0000000084 00000 f\r
+0000000085 00000 f\r
+0000000086 00000 f\r
+0000000087 00000 f\r
+0000000088 00000 f\r
+0000000089 00000 f\r
+0000000090 00000 f\r
+0000000091 00000 f\r
+0000000092 00000 f\r
+0000000096 00000 f\r
+0000026535 00000 n\r
+0000028142 00000 n\r
+0000028173 00000 n\r
+0000000097 00000 f\r
+0000000098 00000 f\r
+0000000099 00000 f\r
+0000000103 00000 f\r
+0000026606 00000 n\r
+0000028024 00000 n\r
+0000028056 00000 n\r
+0000000104 00000 f\r
+0000000105 00000 f\r
+0000000106 00000 f\r
+0000000107 00000 f\r
+0000000108 00000 f\r
+0000000109 00000 f\r
+0000000110 00000 f\r
+0000000111 00000 f\r
+0000000112 00000 f\r
+0000000113 00000 f\r
+0000000114 00000 f\r
+0000000115 00000 f\r
+0000000116 00000 f\r
+0000000117 00000 f\r
+0000000118 00000 f\r
+0000000122 00000 f\r
+0000026680 00000 n\r
+0000027906 00000 n\r
+0000027938 00000 n\r
+0000000123 00000 f\r
+0000000124 00000 f\r
+0000000125 00000 f\r
+0000000129 00000 f\r
+0000026754 00000 n\r
+0000027788 00000 n\r
+0000027820 00000 n\r
+0000000130 00000 f\r
+0000000131 00000 f\r
+0000000132 00000 f\r
+0000000133 00000 f\r
+0000000134 00000 f\r
+0000000135 00000 f\r
+0000000136 00000 f\r
+0000000137 00000 f\r
+0000000138 00000 f\r
+0000000139 00000 f\r
+0000000140 00000 f\r
+0000000141 00000 f\r
+0000000142 00000 f\r
+0000000143 00000 f\r
+0000000144 00000 f\r
+0000000145 00000 f\r
+0000000146 00000 f\r
+0000000147 00000 f\r
+0000000148 00000 f\r
+0000000149 00000 f\r
+0000000150 00000 f\r
+0000000151 00000 f\r
+0000000152 00000 f\r
+0000000153 00000 f\r
+0000000154 00000 f\r
+0000000155 00000 f\r
+0000000156 00000 f\r
+0000000157 00000 f\r
+0000000158 00000 f\r
+0000000159 00000 f\r
+0000000160 00000 f\r
+0000000161 00000 f\r
+0000000162 00000 f\r
+0000000163 00000 f\r
+0000000164 00000 f\r
+0000000165 00000 f\r
+0000000166 00000 f\r
+0000000167 00000 f\r
+0000000168 00000 f\r
+0000000169 00000 f\r
+0000000170 00000 f\r
+0000000171 00000 f\r
+0000000172 00000 f\r
+0000000173 00000 f\r
+0000000174 00000 f\r
+0000000175 00000 f\r
+0000000176 00000 f\r
+0000000177 00000 f\r
+0000000178 00000 f\r
+0000000179 00000 f\r
+0000000183 00000 f\r
+0000026828 00000 n\r
+0000027670 00000 n\r
+0000027702 00000 n\r
+0000000184 00000 f\r
+0000000185 00000 f\r
+0000000186 00000 f\r
+0000000187 00000 f\r
+0000000188 00000 f\r
+0000000189 00000 f\r
+0000000190 00000 f\r
+0000000191 00000 f\r
+0000000192 00000 f\r
+0000000193 00000 f\r
+0000000194 00000 f\r
+0000000195 00000 f\r
+0000000196 00000 f\r
+0000000197 00000 f\r
+0000000198 00000 f\r
+0000000199 00000 f\r
+0000000200 00000 f\r
+0000000201 00000 f\r
+0000000202 00000 f\r
+0000000203 00000 f\r
+0000000204 00000 f\r
+0000000205 00000 f\r
+0000000206 00000 f\r
+0000000207 00000 f\r
+0000000208 00000 f\r
+0000000209 00000 f\r
+0000000210 00000 f\r
+0000000211 00000 f\r
+0000000212 00000 f\r
+0000000213 00000 f\r
+0000000214 00000 f\r
+0000000215 00000 f\r
+0000000216 00000 f\r
+0000000217 00000 f\r
+0000000218 00000 f\r
+0000000219 00000 f\r
+0000000220 00000 f\r
+0000000221 00000 f\r
+0000000222 00000 f\r
+0000000223 00000 f\r
+0000000224 00000 f\r
+0000000225 00000 f\r
+0000000226 00000 f\r
+0000000227 00000 f\r
+0000000228 00000 f\r
+0000000229 00000 f\r
+0000000230 00000 f\r
+0000000231 00000 f\r
+0000000232 00000 f\r
+0000000233 00000 f\r
+0000000237 00000 f\r
+0000026902 00000 n\r
+0000027552 00000 n\r
+0000027584 00000 n\r
+0000000238 00000 f\r
+0000000239 00000 f\r
+0000000240 00000 f\r
+0000000241 00000 f\r
+0000000242 00000 f\r
+0000000243 00000 f\r
+0000000244 00000 f\r
+0000000245 00000 f\r
+0000000246 00000 f\r
+0000000247 00000 f\r
+0000000248 00000 f\r
+0000000249 00000 f\r
+0000000250 00000 f\r
+0000000251 00000 f\r
+0000000252 00000 f\r
+0000000253 00000 f\r
+0000000254 00000 f\r
+0000000255 00000 f\r
+0000000256 00000 f\r
+0000000257 00000 f\r
+0000000258 00000 f\r
+0000000259 00000 f\r
+0000000260 00000 f\r
+0000000261 00000 f\r
+0000000262 00000 f\r
+0000000263 00000 f\r
+0000000264 00000 f\r
+0000000265 00000 f\r
+0000000266 00000 f\r
+0000000267 00000 f\r
+0000000268 00000 f\r
+0000000269 00000 f\r
+0000000270 00000 f\r
+0000000271 00000 f\r
+0000000272 00000 f\r
+0000000273 00000 f\r
+0000000274 00000 f\r
+0000000275 00000 f\r
+0000000276 00000 f\r
+0000000277 00000 f\r
+0000000278 00000 f\r
+0000000279 00000 f\r
+0000000280 00000 f\r
+0000000281 00000 f\r
+0000000282 00000 f\r
+0000000283 00000 f\r
+0000000284 00000 f\r
+0000000285 00000 f\r
+0000000286 00000 f\r
+0000000287 00000 f\r
+0000000288 00000 f\r
+0000000289 00000 f\r
+0000000290 00000 f\r
+0000000291 00000 f\r
+0000000295 00000 f\r
+0000026976 00000 n\r
+0000027434 00000 n\r
+0000027466 00000 n\r
+0000000296 00000 f\r
+0000000297 00000 f\r
+0000000298 00000 f\r
+0000000299 00000 f\r
+0000000300 00000 f\r
+0000000301 00000 f\r
+0000000302 00000 f\r
+0000000303 00000 f\r
+0000000304 00000 f\r
+0000000305 00000 f\r
+0000000306 00000 f\r
+0000000307 00000 f\r
+0000000308 00000 f\r
+0000000309 00000 f\r
+0000000310 00000 f\r
+0000000311 00000 f\r
+0000000312 00000 f\r
+0000000313 00000 f\r
+0000000314 00000 f\r
+0000000315 00000 f\r
+0000000316 00000 f\r
+0000000317 00000 f\r
+0000000318 00000 f\r
+0000000319 00000 f\r
+0000000320 00000 f\r
+0000000321 00000 f\r
+0000000322 00000 f\r
+0000000323 00000 f\r
+0000000324 00000 f\r
+0000000325 00000 f\r
+0000000326 00000 f\r
+0000000327 00000 f\r
+0000000328 00000 f\r
+0000000329 00000 f\r
+0000000330 00000 f\r
+0000000331 00000 f\r
+0000000332 00000 f\r
+0000000333 00000 f\r
+0000000334 00000 f\r
+0000000335 00000 f\r
+0000000336 00000 f\r
+0000000337 00000 f\r
+0000000338 00000 f\r
+0000000339 00000 f\r
+0000000340 00000 f\r
+0000000341 00000 f\r
+0000000342 00000 f\r
+0000000343 00000 f\r
+0000000344 00000 f\r
+0000000345 00000 f\r
+0000000346 00000 f\r
+0000000347 00000 f\r
+0000000348 00000 f\r
+0000000349 00000 f\r
+0000000353 00001 f\r
+0000027050 00000 n\r
+0000027316 00000 n\r
+0000027348 00000 n\r
+0000000354 00000 f\r
+0000000355 00000 f\r
+0000000356 00000 f\r
+0000000357 00000 f\r
+0000000358 00000 f\r
+0000000359 00000 f\r
+0000000360 00000 f\r
+0000000361 00000 f\r
+0000000362 00000 f\r
+0000000363 00000 f\r
+0000000364 00000 f\r
+0000000365 00000 f\r
+0000000366 00000 f\r
+0000000367 00000 f\r
+0000000368 00000 f\r
+0000000369 00000 f\r
+0000000370 00000 f\r
+0000000371 00000 f\r
+0000000372 00000 f\r
+0000000373 00000 f\r
+0000000374 00000 f\r
+0000000375 00000 f\r
+0000000376 00000 f\r
+0000000377 00000 f\r
+0000000378 00000 f\r
+0000000379 00000 f\r
+0000000380 00000 f\r
+0000000381 00000 f\r
+0000000382 00000 f\r
+0000000383 00000 f\r
+0000000384 00000 f\r
+0000000385 00000 f\r
+0000000386 00000 f\r
+0000000387 00000 f\r
+0000000388 00000 f\r
+0000000389 00000 f\r
+0000000390 00000 f\r
+0000000391 00000 f\r
+0000000392 00000 f\r
+0000000393 00000 f\r
+0000000394 00000 f\r
+0000000395 00000 f\r
+0000000396 00000 f\r
+0000000397 00001 f\r
+0000000398 00000 f\r
+0000000399 00000 f\r
+0000000400 00000 f\r
+0000000406 00000 f\r
+0000034768 00000 n\r
+0000034844 00000 n\r
+0000035022 00000 n\r
+0000035933 00000 n\r
+0000052842 00000 n\r
+0000000411 00001 f\r
+0000028954 00000 n\r
+0000027124 00000 n\r
+0000027198 00000 n\r
+0000027230 00000 n\r
+0000000412 00001 f\r
+0000000413 00001 f\r
+0000000415 00001 f\r
+0000034140 00000 n\r
+0000000417 00001 f\r
+0000031078 00000 n\r
+0000000426 00001 f\r
+0000032832 00000 n\r
+0000032895 00000 n\r
+0000033125 00000 n\r
+0000033180 00000 n\r
+0000033818 00000 n\r
+0000033964 00000 n\r
+0000034061 00000 n\r
+0000031306 00000 n\r
+0000000427 00001 f\r
+0000000429 00001 f\r
+0000033754 00000 n\r
+0000000430 00001 f\r
+0000000432 00001 f\r
+0000031673 00000 n\r
+0000000433 00001 f\r
+0000000435 00001 f\r
+0000033690 00000 n\r
+0000000436 00001 f\r
+0000000442 00001 f\r
+0000033263 00000 n\r
+0000033409 00000 n\r
+0000033530 00000 n\r
+0000033611 00000 n\r
+0000032040 00000 n\r
+0000000443 00001 f\r
+0000000445 00001 f\r
+0000032768 00000 n\r
+0000000446 00001 f\r
+0000000449 00001 f\r
+0000032404 00000 n\r
+0000031192 00000 n\r
+0000000450 00001 f\r
+0000000451 00001 f\r
+0000000000 00001 f\r
+0000029401 00000 n\r
+0000030659 00000 n\r
+0000076660 00000 n\r
+0000034204 00000 n\r
+0000034254 00000 n\r
+0000030726 00000 n\r
+0000000367 00000 n\r
+trailer\r<</Size 459/Root 1 0 R/Info 454 0 R/ID[<A3DACB2CAA994ADF839A177E1EF222F1><94FD791731084F1087F9FE9BDDF38204>]>>\rstartxref\r76784\r%%EOF\r
\ No newline at end of file
diff --git a/doc/gfx_and_css/logo.pdf b/doc/gfx_and_css/logo.pdf
new file mode 100644 (file)
index 0000000..593dca4
--- /dev/null
@@ -0,0 +1,201 @@
+%PDF-1.4\r%âãÏÓ\r
+1 0 obj\r<</Metadata 50 0 R/Pages 2 0 R/Type/Catalog>>\rendobj\r50 0 obj\r<</Subtype/XML/Length 26031/Type/Metadata>>stream\r
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.277092, Fri Feb 23 2007 14:16:18        ">
+   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+      <rdf:Description rdf:about=""
+            xmlns:dc="http://purl.org/dc/elements/1.1/">
+         <dc:format>application/pdf</dc:format>
+         <dc:title>
+            <rdf:Alt>
+               <rdf:li xml:lang="x-default">Netatalk_Logo</rdf:li>
+            </rdf:Alt>
+         </dc:title>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xap="http://ns.adobe.com/xap/1.0/"
+            xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/">
+         <xap:CreatorTool>Adobe Illustrator CS3</xap:CreatorTool>
+         <xap:CreateDate>2009-06-04T10:50:16-07:00</xap:CreateDate>
+         <xap:ModifyDate>2009-06-04T10:50:16-07:00</xap:ModifyDate>
+         <xap:MetadataDate>2009-06-04T10:50:16-07:00</xap:MetadataDate>
+         <xap:Thumbnails>
+            <rdf:Alt>
+               <rdf:li rdf:parseType="Resource">
+                  <xapGImg:width>256</xapGImg:width>
+                  <xapGImg:height>256</xapGImg:height>
+                  <xapGImg:format>JPEG</xapGImg:format>
+                  <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW&#xA;du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT&#xA;l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj&#xA;PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9&#xA;Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A&#xA;VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ&#xA;IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8&#xA;Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man&#xA;9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd&#xA;II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8&#xA;X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l&#xA;jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM&#xA;VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN&#xA;ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj&#xA;F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk&#xA;Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9&#xA;/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8&#xA;1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd&#xA;WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG&#xA;D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui&#xA;rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+&#xA;bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3&#xA;Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd&#xA;YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8&#xA;TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P&#xA;gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA&#xA;dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu&#xA;w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK&#xA;s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma&#xA;hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t&#xA;dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q&#xA;7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz&#xA;fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx&#xA;aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz&#xA;KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr&#xA;G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5&#xA;5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu&#xA;DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE&#xA;dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0&#xA;b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG&#xA;syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW&#xA;8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M&#xA;aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz&#xA;DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj&#xA;irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx&#xA;V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF&#xA;PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE&#xA;v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI&#xA;qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2&#xA;KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ&#xA;vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp&#xA;xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C&#xA;81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5&#xA;Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd&#xA;+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/&#xA;ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX&#xA;UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/&#xA;AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k&#xA;9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx&#xA;Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4&#xA;o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa&#xA;KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ&#xA;/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW&#xA;o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf&#xA;lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1&#xA;Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na&#xA;SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc&#xA;Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0&#xA;4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg&#xA;k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr&#xA;+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD&#xA;6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM&#xA;nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn&#xA;v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu&#xA;/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL&#xA;79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB&#xA;1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk&#xA;rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw&#xA;zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf&#xA;/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt&#xA;iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+&#xA;r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb&#xA;Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L&#xA;dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te&#xA;SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1&#xA;Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/&#xA;AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8&#xA;/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o&#xA;pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW&#xA;+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2&#xA;5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s&#xA;8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC&#xA;ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/&#xA;8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p&#xA;8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq&#xA;wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A&#xA;Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu&#xA;+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA&#xA;8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h&#xA;f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7&#xA;uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK&#xA;t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n&#xA;cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2&#xA;7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX&#xA;2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX&#xA;u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z&#xA;9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc&#xA;8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba&#xA;qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69&#xA;piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH&#xA;/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq&#xA;ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK&#xA;se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/&#xA;1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A&#xA;kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd&#xA;5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW&#xA;H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2&#xA;vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd&#xA;5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn&#xA;/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV&#xA;fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5&#xA;x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13&#xA;y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A&#xA;bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/&#xA;/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4&#xA;NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x&#xA;i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM&#xA;AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY&#xA;q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp&#xA;flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L&#xA;wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+&#xA;RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP&#xA;OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV&#xA;fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q&#xA;8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT&#xA;W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE&#xA;tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo&#xA;6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+&#xA;Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A&#xA;yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5&#xA;OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn&#xA;/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6&#xA;ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5&#xA;f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+&#xA;f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7&#xA;A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY&#xA;UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q&#xA;pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp&#xA;HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf&#xA;9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv&#xA;y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380&#xA;tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl&#xA;djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK&#xA;on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr&#xA;9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/&#xA;HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn&#xA;fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV&#xA;2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8&#xA;66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A&#xA;oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3&#xA;/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj&#xA;8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7&#xA;UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm&#xA;Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h&#xA;wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV&#xA;REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/&#xA;8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl&#xA;zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH&#xA;NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/&#xA;AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq&#xA;2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0&#xA;r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr&#xA;Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/&#xA;AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd&#xA;kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN&#xA;/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/&#xA;AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q&#xA;ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl&#xA;pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx&#xA;+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5&#xA;oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl&#xA;pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+&#xA;kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8&#xA;j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM&#xA;akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI&#xA;rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib&#xA;r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr&#xA;p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE&#xA;v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d&#xA;FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN&#xA;Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV&#xA;dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0&#xA;NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a&#xA;1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA&#xA;6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+&#xA;fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8&#xA;Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N&#xA;j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q&#xA;4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF&#xA;7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu&#xA;IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/&#xA;zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK&#xA;u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/&#xA;AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3&#xA;/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE&#xA;gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+&#xA;g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m&#xA;aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN&#xA;jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX&#xA;Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81&#xA;a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W&#xA;O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ&#xA;hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV&#xA;vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS&#xA;7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj&#xA;odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN&#xA;B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH&#xA;cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK&#xA;UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj&#xA;/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f&#xA;aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9&#xA;WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L&#xA;Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB&#xA;V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G&#xA;1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7&#xA;+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/&#xA;u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq&#xA;+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq&#xA;eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+&#xA;cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G&#xA;Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz&#xA;svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35&#xA;V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv&#xA;JvlTyvam18vaTa6XCQA/1aJUZ6dDI4HNz7sTiqc4q7FXYq7FXYq7FXYq7FXYq7FX/9k=</xapGImg:image>
+               </rdf:li>
+            </rdf:Alt>
+         </xap:Thumbnails>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/"
+            xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#">
+         <xapMM:DocumentID>uuid:94E82CBF528711DE830DC7B65C12678A</xapMM:DocumentID>
+         <xapMM:InstanceID>uuid:a8158099-b802-4d00-acfd-b4ab575a94be</xapMM:InstanceID>
+         <xapMM:DerivedFrom rdf:parseType="Resource">
+            <stRef:instanceID>uuid:94E82CBE528711DE830DC7B65C12678A</stRef:instanceID>
+            <stRef:documentID>uuid:1B95FEC551E111DEA13AAE68EE5F3A4D</stRef:documentID>
+         </xapMM:DerivedFrom>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:xapTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+            xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+            xmlns:xapG="http://ns.adobe.com/xap/1.0/g/">
+         <xapTPg:NPages>1</xapTPg:NPages>
+         <xapTPg:HasVisibleTransparency>True</xapTPg:HasVisibleTransparency>
+         <xapTPg:HasVisibleOverprint>False</xapTPg:HasVisibleOverprint>
+         <xapTPg:MaxPageSize rdf:parseType="Resource">
+            <stDim:w>3.000000</stDim:w>
+            <stDim:h>3.000000</stDim:h>
+            <stDim:unit>Inches</stDim:unit>
+         </xapTPg:MaxPageSize>
+         <xapTPg:PlateNames>
+            <rdf:Seq>
+               <rdf:li>Black</rdf:li>
+            </rdf:Seq>
+         </xapTPg:PlateNames>
+         <xapTPg:SwatchGroups>
+            <rdf:Seq>
+               <rdf:li rdf:parseType="Resource">
+                  <xapG:groupName>Default Swatch Group</xapG:groupName>
+                  <xapG:groupType>0</xapG:groupType>
+               </rdf:li>
+            </rdf:Seq>
+         </xapTPg:SwatchGroups>
+      </rdf:Description>
+      <rdf:Description rdf:about=""
+            xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+         <pdf:Producer>Adobe PDF library 8.00</pdf:Producer>
+      </rdf:Description>
+   </rdf:RDF>
+</x:xmpmeta>
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                                                                                                    
+                           
+<?xpacket end="w"?>\rendstream\rendobj\r2 0 obj\r<</Count 1/Type/Pages/Kids[5 0 R]>>\rendobj\r5 0 obj\r<</Parent 2 0 R/Contents 47 0 R/BleedBox[0.0 0.0 216.0 216.0]/ArtBox[16.0 18.25 200.0 202.25]/Group 48 0 R/MediaBox[0.0 0.0 216.0 216.0]/TrimBox[0.0 0.0 216.0 216.0]/Resources<</XObject<</Fm0 20 0 R/Fm1 26 0 R/Fm2 36 0 R/Fm3 42 0 R>>/Properties<</MC0<</Color[65535 20224 20224]/Visible true/Editable true/Dimmed false/Preview true/Printed true/Title(Layer 2)>>>>/ExtGState<</GS0 11 0 R/GS1 43 0 R>>>>/Type/Page>>\rendobj\r47 0 obj\r<</Length 1191/Filter/FlateDecode>>stream\r
+H\89\94\8eÝ6\fÝû+ô\ 3Ö\88¢\1eÔ¶IÛM³\bºèº\18$-ÐL\8bI\81\ 2ùû\1eR¶,ûÞ;E1\98±)¾\ e_2çé§_¿}úê\9e\vî»÷ïÜòº\ 4\17©Øïª\7f¾~Z~q\7fâÔ~|åìþX\9e~ü9¸ßþ^^\1dÙ99
+â¨\89\17\ eä\9e_LþeY\93ø\16Uf\15ñ¹6·ró%ËAëSÜó2\ e\88«Ï0²ë®T«§ÜÔÈþú¼\\99Wí«ù\8bû\81K\7f\9e\97ÏËÇ-BBtST¾&\8e®D/UÚ\14\19ù\84\90\93\8f\11\89*\90* rV\ 4F\14Ï\924.
+¾      ;*>\17\0h8o\8e\19T\85pó"Ñ¥âc5áê\13Þrö\89\18\ 6Cv\82¿A\91'_[r\ 4{A3°rð9%'\b\11yÈ®
+t`8±'©®Àk²ôæ\80\83âjô­"o¹y\ 2Ê\9a})\11\ 1ÀHr\9aË\14U\1a´Ä¬\a\11.Ö"\9e\82ñ#\8c*ÉE\94d&\15¯Á\97Äz ¶Õ\a\99²\14\aÕNæ.\8d yâ7_CÝ\95\85\90.\9alK\ 4\862|\83Ì1\rd »ô\ 6¼A\9dDãJAó\e\82\1a\0jò@ÃwcW\92çjæ)"qªÑk¸\12J\12\11KSE£\93oРBT8P\95\1cÌ\b|\17ëA-\rTrÚè\98\8c\9fòÐàbFS0· Åh\12\930\v\82ºË@\95È\ eP>Ö0Р­FÌ_й$´/c\86Ð}\9b4R(ÚíH¬ñ{Ô@â\ 5Ãðm±£Xó8úb\8d\19j\9a\8c¹L\a½Ç×ÞäÚ÷ÿL\93\12N\93\121Whè\86\19\8f1   \8eÉ£\91¾,\ 5W\ 3\ f\8aì\89n?\88ÁB\a\v_É ¯\98$ªöZÌ\e¥ñª\87¿\eº\ 3SF/d\81\9b\98Íù@\85\11eäÂlöWL0\95\ 2\1aÞR¼!\91\11\ 5\ f\ ed;@a5gWºÇ¸Eò\85Ø\ 3\rÇë5\10\fTÎé^v\11Y\10Í\ 2\8a\fWzd\11Í\9bò\95\1cÒ¢Çgê^B   \ 5\ 3ë\ 1\fînµDWzö{K\ eiÜ2å\86¼\v$!#øædÓ\9f/eíª\88«Ã\9cÇ©50ÈFé\939©4®\ 2hcÞ\e\93©\r²©­]\1aå±\16\9aieg_s÷¤¦\ 6ÉgÝ\eø\99\røãi9Ï@§Ê)igên\92
\19þÿ\9f\17į¡¨å£N:Üå \93\86Ù{Á\98\9a°Ð\aYS\18\ e\95â\18â\e\94¸¡©\84èìâ\8dq.'¾¤¡\ f\ 5%\f[\99\ e´¬(\9b~\82#\YUõ.n\ eµ\95~;Ô2\b´K\1e¢V5ª<Ñè\10]!ô»T{ófû\88\8c\83\8b\81\1e\v\82/2q61|ZbÃäáº\15Ý\9e>/\r\8c&C\0V
+>¦\93\80æ@«8$Ø\17|þO\12\v\f\ 1Ât׳@Ñì\1d\12°ØN\12¯ºµ\91nmO?¼\ 4÷þ¯\19:V       äé\rè]à-è\9bÄcè\9bÀ\eÐ7\89·¡S\87N\rÝ\81*`=j\ 1\1a\14"«ÿXë\ 6\1e_E`Ù$\14z>ó\11}\eü\ eüÄ',i´ó;î\99/ØåÚÎî gö     u<£ÆvAú\81~\8cz\93x\88zã?D½ñ\1f¡ÞØÿ\81\9a;껫±-üÀ¢ËÓ´ðcci}áÇ`\ 2Ç\8a\85¼Ö\83´§­jû\89Ö½aûÜuWÂ~Y#Û¿½báWf<N®Ú7öÏþ7õ}áWK\9bä\8b\11:7\84\r\v«\9d\90ý_Q\8c\e\8e×¾DËÄ\gÍu²©\8b((Ýò3\9e]­ú\82¥-lÏçesÕÉIüdG/\98ï?à\1f³\8fË¿\ 2\f\0~ĨÇ\rendstream\rendobj\r48 0 obj\r<</I false/K false/CS/DeviceCMYK/S/Transparency>>\rendobj\r11 0 obj\r<</OPM 1/BM/Normal/CA 1.0/OP false/SMask/None/ca 1.0/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r43 0 obj\r<</OPM 1/BM/Normal/CA 0.5/OP false/SMask/None/ca 0.5/AIS false/op false/Type/ExtGState/SA true>>\rendobj\r20 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 9 0 R/Resources<</Shading<</Sh0 17 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[106.997 139.145 150.43 135.867]>>stream\r
+q
+150.43 135.867 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 137.5058594 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r26 0 obj\r<</Subtype/Form/Length 129/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 23 0 R/Resources<</Shading<</Sh0 17 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[106.997 133.854 150.43 130.576]>>stream\r
+q
+150.43 130.576 -43.433 3.278 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+43.4326172 0 0 -43.4326172 106.9970703 132.2148438 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r36 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 29 0 R/Resources<</Shading<</Sh0 32 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[39.6411 84.9033 88.6143 81.626]>>stream\r
+q
+39.641 84.903 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 83.2646484 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r42 0 obj\r<</Subtype/Form/Length 126/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Group 39 0 R/Resources<</Shading<</Sh0 32 0 R>>/ColorSpace<</CS0 13 0 R>>/ExtGState<</GS0 11 0 R>>>>/BBox[39.6411 90.1943 88.6143 86.917]>>stream\r
+q
+39.641 90.194 48.973 -3.277 re
+W n
+q
+0 g
+1 i 
+/GS0 gs
+-48.9736328 0 0 48.9736328 88.6142578 88.5556641 cm
+BX /Sh0 sh EX Q
+Q
+\rendstream\rendobj\r39 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r13 0 obj\r[/DeviceN[/Black]/DeviceCMYK 14 0 R 15 0 R]\rendobj\r14 0 obj\r<</Length 91/FunctionType 4/Filter/FlateDecode/Domain[0.0 1.0]/Range[0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]>>stream\r
+H\89ª6Ô3\0\ 3\ 5#\ 5C\85¢ü\9c\1c\ 5bD\f\142óRR+\102\ÉeE
\15É\19
+Å¥I\b
+ºèf (4\ 5\ 2Ë\9b\10Pi\ 2\18S!ªåÆ0\95F\ 4\8c4\82[^\90_ P\v\10`\0.§G±\rendstream\rendobj\r15 0 obj\r<</Subtype/NChannel/Process 16 0 R>>\rendobj\r16 0 obj\r<</Components[/Cyan/Magenta/Yellow/Black]/ColorSpace/DeviceCMYK>>\rendobj\r32 0 obj\r<</ColorSpace 13 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 33 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r33 0 obj\r<</FunctionType 3/Encode[1.0 0.0 0.0 1.0]/Domain[0.0 1.0]/Functions[34 0 R 35 0 R]/Bounds[0.967026]>>\rendobj\r34 0 obj\r<</C0[0.735]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r35 0 obj\r<</C0[0.735]/C1[0.735]/FunctionType 2/N 1.0/Domain[0.0 1.0]>>\rendobj\r29 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r23 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r17 0 obj\r<</ColorSpace 13 0 R/AntiAlias false/Coords[0.0 0.0 1.0 0.0]/Function 18 0 R/Extend[true true]/Domain[0.0 1.0]/ShadingType 2>>\rendobj\r18 0 obj\r<</FunctionType 3/Encode[1.0 0.0]/Domain[0.0 1.0]/Functions[19 0 R]/Bounds[]>>\rendobj\r19 0 obj\r<</C0[1.0]/C1[0.0]/FunctionType 2/N 1.01602/Domain[0.0 1.0]>>\rendobj\r9 0 obj\r<</I false/K false/S/Transparency/Type/Group>>\rendobj\r49 0 obj\r<</CreationDate(D:20090604105016-07'00')/Creator(Adobe Illustrator CS3)/Producer(Adobe PDF library 8.00)/ModDate(D:20090604105016-07'00')/Title(Netatalk_Logo)>>\rendobj\rxref\r0 51\r0000000003 65535 f\r
+0000000016 00000 n\r
+0000026186 00000 n\r
+0000000004 00001 f\r
+0000000006 00000 f\r
+0000026237 00000 n\r
+0000000007 00001 f\r
+0000000008 00001 f\r
+0000000010 00001 f\r
+0000031007 00000 n\r
+0000000012 00001 f\r
+0000027992 00000 n\r
+0000000021 00001 f\r
+0000029722 00000 n\r
+0000029782 00000 n\r
+0000030011 00000 n\r
+0000030064 00000 n\r
+0000030691 00000 n\r
+0000030834 00000 n\r
+0000030929 00000 n\r
+0000028218 00000 n\r
+0000000022 00001 f\r
+0000000024 00001 f\r
+0000030628 00000 n\r
+0000000025 00001 f\r
+0000000027 00001 f\r
+0000028579 00000 n\r
+0000000028 00001 f\r
+0000000030 00001 f\r
+0000030565 00000 n\r
+0000000031 00001 f\r
+0000000037 00001 f\r
+0000030146 00000 n\r
+0000030289 00000 n\r
+0000030407 00000 n\r
+0000030487 00000 n\r
+0000028941 00000 n\r
+0000000038 00001 f\r
+0000000040 00001 f\r
+0000029659 00000 n\r
+0000000041 00001 f\r
+0000000044 00001 f\r
+0000029300 00000 n\r
+0000028105 00000 n\r
+0000000045 00001 f\r
+0000000046 00001 f\r
+0000000000 00001 f\r
+0000026665 00000 n\r
+0000027926 00000 n\r
+0000031069 00000 n\r
+0000000077 00000 n\r
+trailer\r<</Size 51/Root 1 0 R/Info 49 0 R/ID[<8382E707CCB44A8FBBDD373DCFC0BC17><2861F2D7C5014E0FA17536D73E45D784>]>>\rstartxref\r31246\r%%EOF\r
\ No newline at end of file
diff --git a/doc/gfx_and_css/logo.png b/doc/gfx_and_css/logo.png
new file mode 100644 (file)
index 0000000..8138256
Binary files /dev/null and b/doc/gfx_and_css/logo.png differ
diff --git a/doc/gfx_and_css/netatalk.css b/doc/gfx_and_css/netatalk.css
new file mode 100644 (file)
index 0000000..8096a61
--- /dev/null
@@ -0,0 +1,251 @@
+BODY {
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       background-color: white;
+       font-size: 1em;
+       margin-left: 15px;
+       margin-right: 15px;
+}
+
+.pdparam{
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       font-size: 12px;
+}
+
+TD {
+       /* font-family: helvetica, arial, "lucida sans", sans-serif; */
+       font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;;
+       font-size: 12px;
+}
+
+H1, H2, H3 {
+       font-size: 130%;
+       padding: 2px;
+       margin-top: 0px;
+}
+
+H1 {
+       background-color: #424242;
+       color: #FFFFFF;
+}
+
+H2 {
+       background-color: #424242;
+       color: #FFFFFF;
+       text-decoration: none;
+}
+
+H3 {
+       background-color: #330000;
+       color: #FFFFFF;
+}
+
+H4 {
+       color: #330000;
+       font-size: 120%;
+}
+
+H5 {
+       font-size: 100%;
+       color: #330000;
+}
+
+.term {
+       color: black;
+}
+
+TR.qandadiv TD {
+       padding-top: 1em;
+}
+
+DIV.navheader {
+       font-size: 80%;
+       margin-top: 15px;
+}
+
+div.titlepage {
+       margin-top: 15px;
+}
+
+DIV.navheader th{
+       font-size: 100%;
+}
+
+DIV.navfooter {
+       font-size: 80%;
+}
+
+A:link {
+       color: #660000;
+}
+
+A:visited {
+       color: #CC0000;
+}
+
+A:active {
+       color: #FF0033;
+}
+
+TR.question {
+       color: #33C;
+       font-weight: bold;
+}
+
+TR.question TD {
+       padding-top: 1em;
+}
+
+DIV.variablelist {
+       padding-left: 2em;
+       color: #33C;
+}
+
+P {
+       color: black;
+}
+
+DIV.caution, DIV.tip, DIV.important {
+       border: dashed 1px;
+       background-color: #EEEEFF;
+       width: 60em;
+       padding: 5px;
+}
+
+PRE.programlisting, PRE.screen {
+       border: #630 1px dashed;
+       color: #993300;
+       padding: 2px;
+       font-size: 120%;
+}
+DIV.warning { 
+       border: dashed 1px;
+       background-color: #BFBFBF;
+       width: 60em;
+       padding: 5px;
+} 
+
+DIV.note { 
+       border: dashed 1px;
+       background-color: #f2f2f2;
+       width: 60em;
+       padding: 5px;
+} 
+
+DIV.author {
+       padding-top: 1em;
+}
+
+DIV.revhistory table{
+       font-size: 12px;
+       border-collapse: collapse;
+       border-spacing: 0;
+       border: 0px;
+}
+
+DIV.revhistory tr {
+       border: 0px;
+}
+
+DIV.revhistory td {
+       border: 0px;
+}
+
+DIV.revhistory th {
+       border: 0px;
+}
+
+div.book, div.chapter, div.refentry {
+       font-size: 12px;
+}
+
+div.indexdiv {
+       font-size: 12px;
+}
+
+tt {
+       font-size: 12px;
+}
+
+/**
+ * netatalk
+ */
+/* definitions for the header */
+div#header {
+    margin-top: 15px;
+    margin-left: -15px;
+    margin-right: -15px;
+    height: 109px;
+    white-space: nowrap;
+    background-color: #424242;
+    background-image: url(/gfx/bg.gif);
+    background-repeat: no-repeat;
+    background-position: 380px 0px;
+}
+
+div#logo {
+       position: absolute;
+       margin-top: 0px;
+       margin-left: 25px;
+       width: 225px;
+       height: 109px;
+       padding: 0px;
+       background-image: url(/gfx/netatalklogo.gif);
+       background-repeat: no-repeat;
+}
+
+#logo img {
+    border: 0px;
+}
+
+div#menlinks {
+       position: absolute;
+       left: 272px;
+       top: 102px;
+       font-family: Geneva, Arial, Helvetica, sans-serif;
+       font-size: 12px;
+       font-weight: normal;
+       color: #FFFFFF;
+       text-decoration: none;
+       white-space: nowrap;
+
+}
+
+div#menlinks a, div#menlinks a:visited {
+    color: #FFFFFF;
+    margin-right: 15px;
+    text-decoration: none;
+}
+
+#menlinks img {
+   border: 0px;
+}
+
+.italic {
+        font-family: Geneva, Arial, Helvetica, sans-serif;
+        font-size: 11px;
+        font-style: italic;
+        font-weight: normal;
+        color: #000000;
+        text-decoration: none;
+}
+
+a, a:visited {
+  text-decoration: none;
+  color: #660000;
+}
+
+/* definitions for the footer */
+div.footer {
+       margin-left: 210px;
+       margin-top: 22px;
+       font-size: 75%;
+       width: 145px;
+       text-align: left;
+       white-space: nowrap;
+}
+
+.footer img {
+   padding-right: 5px;
+}
diff --git a/doc/html.xsl.in b/doc/html.xsl.in
new file mode 100644 (file)
index 0000000..b2fbfd3
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version='1.0'?> 
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
+       <xsl:import href="@DOCBOOK_ROOT@/xhtml/chunk.xsl"/> 
+       <xsl:param name="use.id.as.filename" select="'1'"/>
+       <xsl:param name="chunk.section.depth" select="0"></xsl:param>
+       <xsl:param name="chunk.separate.lots" select="1"></xsl:param>
+       <xsl:param name="html.stylesheet" select="'http://netatalk.sourceforge.net/css/netatalk.css'"/>
+
+       <xsl:template name="user.header.navigation">
+               <xsl:variable name="codefile" select="document('netatalk.html',/)"/>
+               <xsl:copy-of select="$codefile/*/node()"/>
+       </xsl:template>
+</xsl:stylesheet>
diff --git a/doc/man.xsl.in b/doc/man.xsl.in
new file mode 100644 (file)
index 0000000..fca9fec
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version='1.0'?> 
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
+<xsl:import href="@DOCBOOK_ROOT@/manpages/docbook.xsl"/> 
+
+<!-- * Collect date from <refmiscinfo class="date"> -->
+<xsl:param name="refentry.date.profile.enabled">1</xsl:param>
+<xsl:param name="refentry.date.profile">
+  (//refmiscinfo[@class='date'])[last()]
+</xsl:param>
+
+<!-- * Suppress extra :VERSION: -->
+<xsl:param name="refentry.version.suppress">1</xsl:param>
+
+<!-- * Example without numbering -->
+<xsl:param name="local.l10n.xml" select="document('')"/>
+<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
+<l:l10n language="en">
+ <l:context name="title">
+    <l:template name="example" text="Example.&#160;%t"/>
+ </l:context>
+ <l:context name="title-numbered">
+    <l:template name="example" text="Example.&#160;%t"/>
+ </l:context>
+</l:l10n>
+</l:i18n>
+</xsl:stylesheet>
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
new file mode 100644 (file)
index 0000000..4bc3be5
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = man1 man5 man8
\ No newline at end of file
diff --git a/doc/manpages/man1/.gitignore b/doc/manpages/man1/.gitignore
new file mode 100644 (file)
index 0000000..7943d1d
--- /dev/null
@@ -0,0 +1 @@
+*\.1
\ No newline at end of file
diff --git a/doc/manpages/man1/Makefile.am b/doc/manpages/man1/Makefile.am
new file mode 100644 (file)
index 0000000..dc3d658
--- /dev/null
@@ -0,0 +1,40 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       ad.1 \
+       afpldaptest.1 \
+       afppasswd.1 \
+       afpstats.1 \
+       apple_dump.1 \
+       asip-status.pl.1 \
+       dbd.1 \
+       macusers.1 \
+       megatron.1 \
+       netatalk-config.1 \
+       uniconv.1
+
+EXTRA_DIST = \
+       ad.1.xml \
+       afpldaptest.1.xml \
+       afppasswd.1.xml \
+       afpstats.1.xml \
+       apple_dump.1.xml \
+       asip-status.pl.1.xml \
+       dbd.1.xml \
+       macusers.1.xml \
+       megatron.1.xml \
+       netatalk-config.1.xml \
+       uniconv.1.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.1 : $(MAN_STYLESHEET) %.1.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man1/$@.in
+
+html-local: $(MAN_MANPAGES)
+endif
\ No newline at end of file
diff --git a/doc/manpages/man1/ad.1.xml b/doc/manpages/man1/ad.1.xml
new file mode 100644 (file)
index 0000000..b6a9f55
--- /dev/null
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="ad.1">
+  <refmeta>
+    <refentrytitle>ad</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">02 Sep 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>ad</refname>
+
+    <refpurpose>Netatalk compatible UNIX file utility suite.</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>ad</command>
+
+      <arg choice="req">ls | cp | mv | rm</arg>
+
+      <arg>...</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad</command>
+
+      <arg choice="req">-v | --version</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>ad</command> is a UNIX file utility suite with Netatalk
+    compatibility. AppleDouble<indexterm>
+        <primary>AppleDouble</primary>
+      </indexterm> files in <filename>.AppleDouble</filename> directories and
+    the CNID databases are updated as appropriate.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Available Commands</title>
+
+    <cmdsynopsis>
+      <command>ad ls</command>
+
+      <arg>-dRl<arg>u</arg></arg>
+
+      <arg choice="req">file|dir <arg>...</arg></arg>
+    </cmdsynopsis>
+
+    <para>List files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad cp</command>
+
+      <arg choice="opt">-aipvf</arg>
+
+      <arg choice="req">src_file</arg>
+
+      <arg choice="req">dst_file</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad cp -R</command>
+
+      <arg choice="opt">-aipvf</arg>
+
+      <arg choice="req">src_file|src_directory ...</arg>
+
+      <arg choice="req">dst_directory</arg>
+    </cmdsynopsis>
+
+    <para>Copy files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad mv</command>
+
+      <arg choice="opt">-finv</arg>
+
+      <arg choice="req">src_file</arg>
+
+      <arg choice="req">dst_file</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad mv</command>
+
+      <arg choice="opt">-finv</arg>
+
+      <arg choice="req">src_file|src_directory ...</arg>
+
+      <arg choice="req">dst_directory</arg>
+    </cmdsynopsis>
+
+    <para>Move files and directories.</para>
+
+    <cmdsynopsis>
+      <command>ad rm</command>
+
+      <arg choice="opt">-Rv</arg>
+
+      <arg choice="req">file|directory</arg>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>ad -v|--version</command>
+    </cmdsynopsis>
+
+    <para>Show version.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>ad ls</title>
+
+    <para>List files and directories. Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>Directories are listed as plain files</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>list subdirectories recursively</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-l</term>
+
+        <listitem>
+          <para>Long output, list AFP info</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-u</term>
+
+        <listitem>
+          <para>List UNIX info</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para><emphasis>Long output description</emphasis></para>
+
+    <programlisting>&lt;unixinfo&gt; &lt;FinderFlags&gt; &lt;AFP Attributes&gt; &lt;Color&gt; &lt;Type&gt; &lt;Creator&gt; &lt;CNID from AppleDouble&gt; &lt;name&gt;
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
+
+  d = On Desktop                      (f/d)
+  e = Hidden extension                (f/d)
+  m = Shared (can run multiple times) (f)
+  n = No INIT resources               (f)
+  i = Inited                          (f/d)
+  c = Custom icon                     (f/d)
+  t = Stationery                      (f)
+  s = Name locked                     (f/d)
+  b = Bundle                          (f/d)
+  v = Invisible                       (f/d)
+  a = Alias file                      (f/d)
+
+AFP Attributes:
+
+  y = System                          (f/d)
+  w = No write                        (f)
+  p = Needs backup                    (f/d)
+  r = No rename                       (f/d)
+  l = No delete                       (f/d)
+  o = No copy                         (f)
+
+Note: any letter appearing in uppercase means the flag is set but it's a directory for which the flag is not allowed.</programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>ad cp</title>
+
+    <para>Copy files and directories.</para>
+
+    <para>In the first synopsis form, the cp utility copies the contents of
+    the source_file to the target_file. In the second synopsis form, the
+    contents of each named source_file is copied to the destination
+    target_directory. The names of the files themselves are not changed. If cp
+    detects an attempt to copy a file to itself, the copy will fail.</para>
+
+    <para>Netatalk AFP volumes are detected by means of their ".AppleDesktop"
+    directory which is located in their volume root. When a copy targeting an
+    AFP volume is detected, its CNID database daemon is connected and all
+    copies will also go through the CNID database. AppleDouble files are also
+    copied and created as needed when the target is an AFP volume.</para>
+
+    <para>Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-a</term>
+
+        <listitem>
+          <para>Archive mode. Same as -Rp.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>For each existing destination pathname, remove it and create a
+          new file, without prompting for confirmation regardless of its
+          permis- sions. (The -f option overrides any previous -i or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-i</term>
+
+        <listitem>
+          <para>Cause cp to write a prompt to the standard error output before
+          copying a file that would overwrite an existing file. If the
+          response from the standard input begins with the character 'y' or
+          'Y', the file copy is attempted. (The -i option overrides any pre-
+          vious -f or -n options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>Do not overwrite an existing file. (The -n option overrides
+          any previous -f or -i options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-p</term>
+
+        <listitem>
+          <para>Cause cp to preserve the following attributes of each source
+          file in the copy: modification time, access time, file flags, file
+          mode, user ID, and group ID, as allowed by permissions. If the user
+          ID and group ID cannot be preserved, no error message is displayed
+          and the exit value is not altered.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>If source_file designates a directory, cp copies the directory
+          and the entire subtree connected at that point.If the source_file
+          ends in a /, the contents of the directory are copied rather than
+          the directory itself.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Cause cp to be verbose, showing files as they are
+          copied.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-x</term>
+
+        <listitem>
+          <para>File system mount points are not traversed.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>ad mv</title>
+
+    <para>Move files and directories.</para>
+
+    <para>Move files around within an AFP volume, updating the CNID database
+    as needed. If either:<itemizedlist>
+        <listitem>
+          <para>source or destination is not an AFP volume</para>
+        </listitem>
+
+        <listitem>
+          <para>source AFP volume != destination AFP volume</para>
+        </listitem>
+      </itemizedlist>the files are copied and removed from the source.</para>
+
+    <para>Options:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>Do not prompt for confirmation before overwriting the
+          destination path. (The -f option overrides any previous -i or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-i</term>
+
+        <listitem>
+          <para>Cause mv to write a prompt to standard error before moving a
+          file that would overwrite an existing file. If the response from the
+          standard input begins with the character `y' or `Y', the move is
+          attempted. (The -i option overrides any previous -f or -n
+          options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>Do not overwrite an existing file. (The -n option overrides
+          any previous -f or -i options.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Cause mv to be verbose, showing files after they are
+          moved.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>ad rm</title>
+
+    <para>Remove files and directories.</para>
+
+    <para>The rm utility attempts to remove the non-directory type files
+    specified on the command line. If the files and directories reside on an
+    AFP volume, the corresponding CNIDs are deleted from the volumes
+    database.</para>
+
+    <para>The options are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>-R</term>
+
+        <listitem>
+          <para>Attempt to remove the file hierarchy rooted in each file
+          argument.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Be verbose when deleting files, showing them as they are
+          removed.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Reporting Bugs</title>
+
+    <para>Report bugs to the Netatalk-devel list
+    &lt;netatalk-devel@lists.sourceforge.net&gt;.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>apple_dump</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/afpldaptest.1.xml b/doc/manpages/man1/afpldaptest.1.xml
new file mode 100644 (file)
index 0000000..a7e73cd
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpldaptest.1">
+  <refmeta>
+    <refentrytitle>afpldaptest</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>afpldaptest</refname>
+
+    <refpurpose>Syntactically check ldap parameters in afp.conf</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>afpldaptest<indexterm><primary>afpldaptest</primary></indexterm></command>
+
+      <group choice="req">
+        <arg choice="plain">-u <replaceable>USER</replaceable></arg>
+        <arg choice="plain">-g <replaceable>GROUP</replaceable></arg>
+        <arg choice="plain">-i <replaceable>UUID</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>afpldaptest<indexterm><primary>afpldaptest</primary></indexterm></command>
+
+      <group choice="req">
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-?</arg>
+        <arg choice="plain">-:</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>afpldaptest</command> is a simple command to syntactically
+    check ldap parameters in @pkgconfdir@/afp.conf.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-u</option> <replaceable>USER</replaceable></term>
+
+        <listitem>
+          <para>Show uuid for <replaceable>USER</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-g</option> <replaceable>GROUP</replaceable></term>
+
+        <listitem>
+          <para>Show uuid for <replaceable>GROUP</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-i</option> <replaceable>UUID</replaceable></term>
+
+        <listitem>
+          <para>Show user, group or local-uuid for
+         <replaceable>UUID</replaceable>.</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -?, -:</option></term>
+
+        <listitem>
+          <para>Show the help and exit.</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/afppasswd.1.xml b/doc/manpages/man1/afppasswd.1.xml
new file mode 100644 (file)
index 0000000..e67727f
--- /dev/null
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afppasswd.1">
+  <refmeta>
+    <refentrytitle>afppasswd</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afppasswd</refname>
+
+    <refpurpose>netatalk password maintenance utility</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>afppasswd<indexterm>
+          <primary>afppasswd</primary>
+        </indexterm><indexterm>
+          <primary>UAM</primary>
+
+          <secondary>User Authentication Module</secondary>
+        </indexterm></command>
+
+      <arg choice="opt">-acfn</arg>
+
+      <arg choice="opt"><arg choice="plain">-p
+      <replaceable>passwd</replaceable></arg><arg
+      choice="plain"><replaceable>file</replaceable></arg></arg>
+
+      <arg choice="opt"><arg choice="plain">-u
+      <replaceable>minimum</replaceable></arg><arg
+      choice="plain"><replaceable>uid</replaceable></arg></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>afppasswd</command> allows the maintenance of afppasswd
+    files created by netatalk for use by the uams_randnum.so UAM (providing
+    the "Randnum exchange" and "2-Way Randnum exchange" User Authentication
+    Modules).</para>
+
+    <para><command>afppasswd</command> can either be called by root with
+    parameters, or can be called by local system users with no parameters to
+    change their AFP passwords.</para>
+
+    <note>
+      <para>With this utility you can only change the passwords used by two
+      specific UAMs. As they provide only weak password encryption, the use of
+      the "Randnum exchange" and "2-Way Randnum exchange" UAMs is deprecated
+      unless one has to support very old AFP clients, that can not deal with
+      the more secure "DHCAST128" and "DHX2" UAM instead. Please compare with
+      the <link linkend="authentication">Authentication chapter</link> inside
+      Netatalk's documentation.</para>
+    </note>
+  </refsect1>
+
+  <refsect1>
+    <title>EXAMPLE</title>
+
+    <para>Local user changing their own password:</para>
+
+    <screen><prompt>example%</prompt> <userinput>afppasswd</userinput>
+<computeroutput>Enter NEW AFP password:</computeroutput> <userinput>(hidden)</userinput>
+<computeroutput>Enter NEW AFP password again:</computeroutput> <userinput>(hidden)</userinput>
+<computeroutput>afppasswd: updated password.</computeroutput>
+</screen>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-a</option></term>
+
+        <listitem>
+          <para>Add a new user to the <command>afppasswd</command>
+          file.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-c</option></term>
+
+        <listitem>
+          <para>Create and/or initialize <command>afppasswd</command> file or
+          specific user.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-f</option></term>
+
+        <listitem>
+          <para>Force the current action.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-p</option><replaceable> path</replaceable></term>
+
+        <listitem>
+          <para>Path to <command>afppasswd</command> file.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-n</option></term>
+
+        <listitem>
+          <para>If cracklib support is built into <emphasis
+          remap="B">netatalk</emphasis> this option will cause cracklib
+          checking to be disabled, if the superuser does not want to have the
+          password run against the cracklib dictionary.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-u</option><replaceable> minimum
+        uid</replaceable></term>
+
+        <listitem>
+          <para>This is the minimum <emphasis remap="I">user id</emphasis>
+          (uid) that <command>afppasswd</command> will use when creating
+          users.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
\ No newline at end of file
diff --git a/doc/manpages/man1/afpstats.1.xml b/doc/manpages/man1/afpstats.1.xml
new file mode 100644 (file)
index 0000000..c2e9799
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpstats.1">
+  <refmeta>
+    <refentrytitle>afpstats</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">24 Mar 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>afpstats</refname>
+
+    <refpurpose>List AFP statistics</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>afpstats<indexterm><primary>afpstats</primary></indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>afpstats</command> list AFP statistics via D-Bus IPC.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>NOTE</title>
+
+    <para><command>afpd</command> must support D-Bus. Check it by
+    "<command>afpd -V</command>".</para>
+
+    <para>"<option>afpstats = yes</option>" must be set in
+    <filename>@pkgconfdir@/afp.conf</filename>.</para>
+
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/apple_dump.1.xml b/doc/manpages/man1/apple_dump.1.xml
new file mode 100644 (file)
index 0000000..2457d1d
--- /dev/null
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="apple_dump.1">
+  <refmeta>
+    <refentrytitle>apple_dump</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">16 Jul 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>apple_dump</refname>
+
+    <refpurpose>Dump AppleSingle/AppleDouble format data</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="opt"><arg choice="plain">-a</arg></arg>
+
+      <group choice="opt">
+        <arg choice="plain"><replaceable>FILE</replaceable></arg>
+        <arg choice="plain"><replaceable>DIR</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-e</arg>
+
+      <group choice="plain">
+        <arg choice="plain"><replaceable>FILE</replaceable></arg>
+        <arg choice="plain"><replaceable>DIR</replaceable></arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-f</arg>
+
+      <arg choice="opt"><replaceable>FILE</replaceable></arg>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <arg choice="plain">-d</arg>
+
+      <arg choice="opt"><replaceable>FILE</replaceable></arg>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-help</arg>
+        <arg choice="plain">--help</arg>
+      </group>
+
+      <sbr />
+
+      <command>apple_dump<indexterm><primary>apple_dump</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>apple_dump</command> is a perl script to dump
+    AppleSingle/AppleDouble format data. </para>
+    <para>This script can dump various AppleSingle/AppleDouble data created
+    by mailer, archiver, Mac OS X, Netatalk and so on.</para>
+    <para>With no <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable>, or when <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable> is -, read standard input.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-a</option> [<replaceable>FILE</replaceable>|<replaceable>DIR</replaceable>]</term>
+
+        <listitem>
+          <para>This is default.
+          Dump a AppleSingle/AppleDouble file for
+          <replaceable>FILE</replaceable> or <replaceable>DIR</replaceable>
+          automatically.
+          If FILE is not AppleSingle/AppleDouble format,
+          look for extended attribute, 
+          <replaceable>.AppleDouble/FILE</replaceable> and
+          <replaceable>._FILE</replaceable>.
+          If <replaceable>DIR</replaceable>, look for
+          extended attribute, 
+          <replaceable>DIR/.AppleDouble/.Parent</replaceable> and
+          <replaceable>._DIR</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-e</option> <replaceable>FILE</replaceable>|<replaceable>DIR</replaceable></term>
+
+        <listitem>
+          <para>Dump extended attribute of<replaceable>FILE</replaceable> or <replaceable>DIR</replaceable>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-f</option> [<replaceable>FILE</replaceable>]</term>
+
+        <listitem>
+          <para>Dump <replaceable>FILE</replaceable>. Assume FinderInfo to be FileInfo.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-d</option> [<replaceable>FILE</replaceable>]</term>
+
+        <listitem>
+          <para>Dump <replaceable>FILE</replaceable>. Assume FinderInfo to be DirInfo.</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -help, --help</option></term>
+
+        <listitem>
+          <para>Display the help and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>NOTE</title>
+
+    <para>There is no way to detect whether FinderInfo is FileInfo or DirInfo.
+    By default, apple_dump examines whether file or directory, a parent directory
+    is .AppleDouble, filename is ._*, filename is .Parent, and so on.</para>
+
+    <para>If setting option -e, -f or -d,  assume FinderInfo and doesn't look
+    for another file.</para>
+
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>ad</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>getfattr</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>attr</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>runat</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>getextattr</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>lsextattr</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/asip-status.pl.1.xml b/doc/manpages/man1/asip-status.pl.1.xml
new file mode 100644 (file)
index 0000000..859b063
--- /dev/null
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="asip-status.pl.1">
+  <refmeta>
+    <refentrytitle>asip-status.pl</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">24 Jul 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>asip-status.pl</refname>
+
+    <refpurpose>Queries AFP servers for their capabilities</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>asip-status.pl<indexterm>
+          <primary>asip-status.pl</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+      <arg choice="opt">-i</arg>
+      <arg choice="opt">-x</arg>
+
+      <arg choice="plain">HOSTNAME[:PORT]</arg>
+    </cmdsynopsis>
+
+    <sbr />
+
+    <cmdsynopsis>
+      <command>asip-status.pl<indexterm>
+          <primary>asip-status.pl</primary>
+        </indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+      </group>
+    </cmdsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><emphasis remap="B">asip-status.pl</emphasis> is a perl script that
+    sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and
+    displays the results, namely "Machine type", the server's name, supported
+    AFP versions, UAMs and AFP flags, the "server signature" and the network
+    addresses, the server provides AFP services on.</para>
+
+    <para>When you don't supply :PORT, then the default AFP port, 548, will be
+    used.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-d</option></term>
+
+        <listitem>
+          <para>Enable debug output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-i</option></term>
+
+        <listitem>
+          <para>Show icon if it exists.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-x</option></term>
+
+        <listitem>
+          <para>Enable hex dump output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version.</para>
+        </listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>EXAMPLES</title>
+
+    <para><programlisting><emphasis remap="B">asip-status.pl</emphasis> 192.168.1.15
+AFP reply from 192.168.1.15:548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Macintosh
+AFP versions: AFPVersion 1.1,AFPVersion 2.0,AFPVersion 2.1,AFP2.2
+UAMs: Cleartxt passwrd,Randnum exchange,2-Way Randnum exchange
+Volume Icon &amp; Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsChgPwd
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSuperClient
+Server name: bookchan
+Signature:
+04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  ..e#..e#..e#..e#
+                                                  
+Network address: 192.168.1.15:548 (TCP/IP address and port)
+Network address: 65280.128 (ddp address)
+</programlisting></para>
+
+    <para><programlisting><emphasis remap="B">asip-status.pl</emphasis> myserver:10548
+AFP reply from myserver:10548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Netatalk3.0
+AFP versions: AFP2.2,AFPX03,AFP3.1,AFP3.2,AFP3.3
+UAMs: DHX2,DHCAST128
+Volume Icon &amp; Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSrvrNotifications
+    SupportsOpenDirectory
+    SupportsUTF8Servername
+    SupportsUUIDs
+    SupportsExtSleep
+    SupportsSuperClient
+Server name: myserver
+Signature:
+8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  ...:...>o1....p.
+                                                  
+Network address: 192.168.1.154:10548 (TCP/IP address and port)
+UTF8 Servername: myserver
+</programlisting></para>
+  </refsect1>
+
+  <refsect1>
+    <title>REPORTING BUGS</title>
+
+    <para>Report bugs to the Netatalk-devel list
+    &lt;netatalk-devel@lists.sourceforge.net&gt;.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/dbd.1.xml b/doc/manpages/man1/dbd.1.xml
new file mode 100644 (file)
index 0000000..36b7aba
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="dbd.1">
+  <refmeta>
+    <refentrytitle>dbd</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">28 Dec 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>dbd</refname>
+
+    <refpurpose>CNID database maintenance</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>dbd<indexterm>
+          <primary>dbd</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-fsv</arg>
+
+      <arg choice="plain"><replaceable>volumepath</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>dbd</command> scans all file and directories of AFP
+    volumes, updating the CNID database of the volume. It must be run with
+    appropriate permissions i.e. as root..</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-c</term>
+
+        <listitem>
+          <para>convert from adouble:v2 to adouble:ea</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>delete and recreate CNID database</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-F</term>
+
+        <listitem>
+          <para>location of the afp.conf config file</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-s</term>
+
+        <listitem>
+          <para>scan volume: treat the volume as read only and don't perform
+          any filesystem modifications</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-t</term>
+
+        <listitem>
+          <para>show statistics while running</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>verbose</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>display version info</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID background</title>
+
+    <para>The CNID backends maintains name to ID mappings. If you change a
+    filename outside afpd(8) (shell, samba), the CNID database will not
+    reflect that change. Netatalk tries to recover from such inconsistencies
+    as gracefully as possible.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_dbd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/macusers.1.xml b/doc/manpages/man1/macusers.1.xml
new file mode 100644 (file)
index 0000000..5cfee6f
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="macusers.1">
+  <refmeta>
+    <refentrytitle>macusers</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">13 Oct 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>macusers</refname>
+
+    <refpurpose>List the users connecting via AFP</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>macusers<indexterm><primary>macusers</primary></indexterm></command>
+    </cmdsynopsis>
+
+    <cmdsynopsis>
+      <command>macusers<indexterm><primary>macusers</primary></indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-version</arg>
+        <arg choice="plain">--version</arg>
+        <arg choice="plain">-h</arg>
+        <arg choice="plain">-help</arg>
+        <arg choice="plain">--help</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>macusers</command> list the users connecting via AFP.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, -version, --version</option></term>
+
+        <listitem>
+          <para>Show version and exit</para>
+        </listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-h, -help, --help</option></term>
+
+        <listitem>
+          <para>Display the help and exit</para>
+        </listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/megatron.1.xml b/doc/manpages/man1/megatron.1.xml
new file mode 100644 (file)
index 0000000..20b6405
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="megatron.1">
+  <refmeta>
+    <refentrytitle>megatron</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">02 Sep 2011</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="name">
+    <refname>megatron</refname>
+
+    <refname>unhex</refname>
+
+    <refname>unbin</refname>
+
+    <refname>unsingle</refname>
+
+    <refname>hqx2bin</refname>
+
+    <refname>single2bin</refname>
+
+    <refname>macbinary</refname>
+
+    <refpurpose>Macintosh file format transformer</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>megatron<indexterm><primary>megatron</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unbin<indexterm><primary>unbin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unhex<indexterm><primary>unhex</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>unsingle<indexterm><primary>unsingle</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>hqx2bin<indexterm><primary>hqx2bin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>single2bin<indexterm><primary>single2bin</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+
+      <sbr />
+
+      <command>macbinary<indexterm><primary>macbinary</primary></indexterm></command>
+
+      <arg choice="opt" rep="repeat"><replaceable>sourcefile</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="description">
+    <title>DESCRIPTION</title>
+
+    <para><command>megatron</command> is used to transform files from BinHex,
+    MacBinary, AppleSingle, or <emphasis remap="B" role="bold">netatalk</emphasis> style
+    AppleDouble formats into MacBinary or <emphasis remap="B">netatalk</emphasis>
+    style AppleDouble formats. The <emphasis remap="B">netatalk</emphasis>
+    style AppleDouble format is the file format used by <emphasis remap="B">afpd,</emphasis>
+    the <emphasis remap="B">netatalk</emphasis> Apple Filing Protocol
+    (AppleShare) server. BinHex, MacBinary, and AppleSingle are commonly used
+    formats for transferring Macintosh files between machines via email or
+    file transfer protocols. <command>megatron</command> uses its name to
+    determine what type of transformation is being asked of it.</para>
+
+    <para>If <command>megatron</command> is called as <command>unhex</command>
+    , <command>unbin</command> or <command>unsingle</command>, it tries to
+    convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble
+    format. BinHex is the format most often used to send Macintosh files by
+    e-mail. Usually these files have an extension of &#34;.hqx&#34;. MacBinary
+    is the format most often used by terminal emulators &#34;on the fly&#34;
+    when transferring Macintosh files in binary mode. MacBinary files often
+    have an extension of &#34;.bin&#34;. Some Macintosh LAN-based email
+    packages use uuencoded AppleSingle format to &#34;attach&#34; or
+    &#34;enclose&#34; files in email. AppleSingle files don&#39;t have a
+    standard filename extension.</para>
+
+    <para>If <command>megatron</command> is called as <command>hqx2bin</command>,
+    <command>single2bin</command>, or <command>macbinary</command>, it will
+    try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into
+    MacBinary. This last translation may be useful in moving Macintosh files
+    from your <command>afpd</command> server to some other machine when you
+    can&#39;t copy them from the server using a Macintosh for some reason.</para>
+
+    <para>If <command>megatron</command> is called with any other name, it
+    uses the default translation, namely <command>unhex</command>.</para>
+
+    <para>If no source file is given, or if <emphasis remap="I">sourcefile</emphasis>
+    is `<emphasis remap="" role="bold">-</emphasis>&#39;, and if the
+    conversion is from a BinHex or MacBinary file, <command>megatron</command>
+    will read from standard input.</para>
+
+    <para>The filename used to store any output file is the filename that is
+    encoded in the source file. MacBinary files are created with a
+    &#34;.bin&#34; extension. In the case of conflicts, the old file is
+    overwritten!</para>
+  </refsect1>
+
+  <refsect1 id="options">
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, --version</option></term>
+
+        <listitem>
+          <para>Show version.</para>
+        </listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="see_also">
+    <title>SEE ALSO</title>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/netatalk-config.1.xml b/doc/manpages/man1/netatalk-config.1.xml
new file mode 100644 (file)
index 0000000..69da13f
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="netatalkconfig.1">
+  <refmeta>
+    <refentrytitle>netatalk-config</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">09 June 2001</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+
+    <refmiscinfo class="manual">The Netatalk Project</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>netatalk-config</refname>
+
+    <refpurpose>script to get information about the installed version of
+    netatalk</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>netatalk-config<indexterm><primary>netatalk-config</primary></indexterm></command>
+
+      <arg choice="opt"><arg choice="plain">--prefix </arg><arg choice="opt"><replaceable>=DIR</replaceable></arg></arg>
+
+      <arg choice="opt"><arg choice="plain">--exec_prefix </arg><arg
+      choice="opt"><replaceable>=DIR</replaceable></arg></arg>
+
+      <arg choice="opt">--help</arg>
+
+      <arg choice="opt">--version</arg>
+
+      <arg choice="opt">--libs</arg>
+
+      <arg choice="opt">--libs-dirs</arg>
+
+      <arg choice="opt">--libs-names</arg>
+
+      <arg choice="opt">--cflags</arg>
+
+      <arg choice="opt">--macros</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>netatalk-config</command> is a tool that is used to
+    determine the compiler and linker flags that should be used
+    to compile and link programs that use the <emphasis remap="I">netatalk</emphasis>
+    run-time libraries.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para><command>netatalk-config</command> accepts the following options:</para>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>--help</option></term>
+
+        <listitem>
+          <para>Print a short help for this command and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--version</option></term>
+
+        <listitem>
+          <para>Print the currently installed version of <emphasis remap="I">netatalk</emphasis>
+          on the standard output.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs</option></term>
+
+        <listitem>
+          <para>Print the linker flags that are necessary to link against the
+          <emphasis remap="I">netatalk</emphasis> run-time libraries.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs-dirs</option></term>
+
+        <listitem>
+          <para>Print only the -l/-R part of --libs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--libs-names</option></term>
+
+        <listitem>
+          <para>Print only the -l part of --libs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--cflags</option></term>
+
+        <listitem>
+          <para>Print the compiler flags that are necessary to compile a
+          program linked against the <emphasis remap="I">netatalk</emphasis>
+          run-time libraries.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--macros</option></term>
+
+        <listitem>
+          <para>Print the <emphasis remap="I">netatalk</emphasis> m4
+          directory.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--prefix=PREFIX</option></term>
+
+        <listitem>
+          <para>If specified, use PREFIX instead of the installation prefix
+          that <emphasis remap="I">netatalk</emphasis> was built with when
+          computing the output for the --cflags and --libs options. This
+          option is also used for the exec prefix if --exec-prefix was not
+          specified. This option must be specified before any --libs or
+          --cflags options.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--exec\_prefix=PREFIX</option></term>
+
+        <listitem>
+          <para>If specified, use PREFIX instead of the installation exec
+          prefix that <emphasis remap="I">netatalk</emphasis> was built with
+          when computing the output for the --cflags and --libs options. This
+          option must be specified before any --libs or --cflags options.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>COPYRIGHT</title>
+
+    <para>Copyright © 1998 Owen Taylor</para>
+
+    <para>Permission to use, copy, modify, and distribute this software and
+    its documentation for any purpose and without fee is hereby granted,
+    provided that the above copyright notice appear in all copies and that
+    both that copyright notice and this permission notice appear in supporting
+    documentation.</para>
+
+    <para>Man page adapted for <command>netatalk-config</command> by Sebastian
+    Rittau in 2001.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man1/uniconv.1.xml b/doc/manpages/man1/uniconv.1.xml
new file mode 100644 (file)
index 0000000..d563c28
--- /dev/null
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="uniconv.1">
+
+  <refmeta>
+    <refentrytitle>uniconv</refentrytitle>
+
+    <manvolnum>1</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>uniconv</refname>
+
+    <refpurpose>convert Netatalk volume encoding</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>uniconv<indexterm><primary>uniconv</primary></indexterm></command>
+
+      <arg choice="opt">-ndv</arg>
+
+      <arg choice="plain">-c <replaceable>cnidbackend</replaceable></arg>
+
+      <arg choice="plain">-f <replaceable>fromcode</replaceable></arg>
+
+      <arg choice="plain">-t <replaceable>tocode</replaceable></arg>
+
+      <arg>-m <replaceable>maccode</replaceable></arg>
+
+      <arg choice="plain"><replaceable>volumepath</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>uniconv</command> converts the volume encoding of
+    <replaceable>volumepath</replaceable> from the <replaceable>fromcode</replaceable>
+    to the <replaceable>tocode</replaceable> encoding.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-c</term>
+
+        <listitem>
+          <para>CNID backend used on this volume, usually cdb or dbd. Should
+          match the backend selected with afpd for this volume. If not
+          specified, the default CNID backend "@DEFAULT_CNID_SCHEME@" is
+          used</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>don't HEX encode leading dots (:2e), equivalent to
+          <option>use dots = yes</option> in <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-f</term>
+
+        <listitem>
+          <para>encoding to convert from, use ASCII for HEX encoded volumes</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-h</term>
+
+        <listitem>
+          <para>display help</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-m</term>
+
+        <listitem>
+          <para>Macintosh client codepage, required for HEX encoded volumes.
+          Defaults to "MAC_ROMAN"</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-n</term>
+
+        <listitem>
+          <para>"dry run", don't do any real changes</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-t</term>
+
+        <listitem>
+          <para>volume encoding to convert to, e.g. UTF8</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>verbose output, use twice for maximum logging.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>print version and exit</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>WARNING</title>
+
+    <para>Setting the wrong options might render your data unusable!!! Make
+    sure you know what you are doing. Always backup your data first.</para>
+
+    <para>It is <emphasis role="bold">*strongly*</emphasis> recommended to do
+    a "dry run" first and to check the output for conversion errors.</para>
+
+    <para><citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    should <emphasis>not</emphasis> be running while you change the volume
+    encoding. Remember to change <option>unix charset</option> or
+    <option>vol charset</option> in
+    <citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    to the new codepage, before restarting afpd.</para>
+
+    <para>In case of <emphasis role="bold">MacChineseTraditional</emphasis>,
+    <emphasis role="bold">MacJapanese</emphasis> or
+    <emphasis role="bold">MacKorean</emphasis>,
+    uniconv cannot be used.</para>
+
+    <para><emphasis role="bold">USE AT YOUR OWN RISK!!!</emphasis></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Selectable charsets</title>
+
+    <para>Netatalk provides internal support for UTF-8 (pre- and decomposed)
+    and HEX. If you want to use other charsets, they must be provided by
+    <citerefentry><refentrytitle>iconv</refentrytitle><manvolnum>1</manvolnum></citerefentry></para>
+
+    <para><command>uniconv</command> also knows iso-8859.adapted, an old style
+    1.x NLS widely used. This is only intended for upgrading old volumes,
+    <citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    cannot handle iso-8859.adapted anymore.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID background</title>
+
+    <para>The CNID backends maintains name to ID mappings. If you change a
+    filename outside afpd(8) (shell, samba), the CNID db, i.e. the DIDNAME
+    index, gets inconsistent. Netatalk tries to recover from such
+    inconsistencies as gracefully as possible. The mechanisms to resolve such
+    inconsistencies may fail sometimes, though, as this is not an easy task to
+    accomplish. I.e. if several names in the path to the file or directory
+    have changed, things may go wrong.</para>
+
+    <para>If you change a lot of filenames at once, chances are higher that
+    the afpds fallback mechanisms fail, i.e. files will be assigned new IDs,
+    even though the file hasn't changed. <command>uniconv</command>
+    therefore updates the CNID entry for each file/directory directly after it
+    changes the name to avoid inconsistencies. The two supported backends for
+    volumes, dbd and cdb, use the same CNID db format. Therefore, you
+    <emphasis>could</emphasis> use <command>uniconv</command> with cdb and
+    <command>afpd</command> with dbd later.</para>
+
+    <para><emphasis role="bold">Warning</emphasis>: There must not be two
+    processes opening the CNID database using different backends at once! If a
+    volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
+    <command>uniconv</command> with cdb, the result will be a corrupted CNID
+    database, as the two backends use different locking schemes. You might run
+    into additional problems, e.g. if dbd is compiled with transactions, cdb
+    will not update the transaction logs.</para>
+
+    <para>In general, it is recommended to use the same backend for
+    <command>uniconv</command> you are using with
+    <citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>convert 1.x CAP encoded volume to UTF-8, clients used MacRoman
+    codepage, cnidscheme is dbd:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -c dbd -f ASCII -t UTF8 -m MAC_ROMAN /path/to/share</userinput></screen>
+
+    <para>convert iso8859-1 volume to UTF-8, cnidscheme is cdb:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -c cdb -f ISO-8859-1 -t UTF8 -m MAC_ROMAN /path/to/share</userinput></screen>
+
+    <para>convert 1.x volume using iso8859-1 adapted NLS to HEX encoding:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -f ISO-8859-ADAPTED -t ASCII -m MAC_ROMAN/path/to/share</userinput></screen>
+
+    <para>convert UTF-8 volume to HEX, for MacCyrillic clients:</para>
+
+    <screen><prompt>example%</prompt><userinput> uniconv -f UTF8 -t ASCII -m MAC_CYRILLIC /path/to/share</userinput></screen>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry><refentrytitle>afp.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,<citerefentry><refentrytitle>afpd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,<citerefentry><refentrytitle>iconv</refentrytitle><manvolnum>1</manvolnum></citerefentry>,<citerefentry><refentrytitle>cnid_metad</refentrytitle><manvolnum>8</manvolnum></citerefentry>,<citerefentry><refentrytitle>cnid_dbd</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/.gitignore b/doc/manpages/man5/.gitignore
new file mode 100644 (file)
index 0000000..2fc1d34
--- /dev/null
@@ -0,0 +1 @@
+*\.5
diff --git a/doc/manpages/man5/Makefile.am b/doc/manpages/man5/Makefile.am
new file mode 100644 (file)
index 0000000..82fee7c
--- /dev/null
@@ -0,0 +1,26 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       afp.conf.5 \
+       afp_signature.conf.5 \
+       afp_voluuid.conf.5 \
+       extmap.conf.5
+
+EXTRA_DIST = \
+       afp_signature.conf.5.xml \
+       afp_voluuid.conf.5.xml \
+       afp.conf.5.xml \
+       extmap.conf.5.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.5 : $(MAN_STYLESHEET) %.5.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man5/$@.in
+
+html-local: $(MAN_MANPAGES)
+endif
\ No newline at end of file
diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml
new file mode 100644 (file)
index 0000000..a09eec5
--- /dev/null
@@ -0,0 +1,1970 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp.conf.5">
+  <refmeta>
+    <refentrytitle>afp.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">30 Apr 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp.conf</refname>
+
+    <refpurpose>Netatalk configuration file <indexterm>
+        <primary>afp.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>SYNOPSIS</title>
+
+    <para>The <filename>afp.conf</filename> file is the configuration file for
+    the <emphasis role="bold">Netatalk</emphasis> AFP file server.</para>
+
+    <para>All AFP specific configuration and AFP volume definitions are done
+    via this file.</para>
+  </refsect1>
+
+  <refsect1 id="FILEFORMATSECT">
+    <title>FILE FORMAT</title>
+
+    <para>The file consists of sections and parameters. A section begins with
+    the name of the section in square brackets and continues until the next
+    section begins. Sections contain parameters of the form: <programlisting>
+    <replaceable>name</replaceable> = <replaceable>value </replaceable>
+    </programlisting></para>
+
+    <para>The file is line-based - that is, each newline-terminated line
+    represents either a comment, a section name or a parameter.</para>
+
+    <para>Section and parameter names are case sensitive.</para>
+
+    <para>Only the first equals sign in a parameter is significant. Whitespace
+    before or after the first equals sign is discarded. Leading, trailing and
+    internal whitespace in section and parameter names is irrelevant. Leading
+    and trailing whitespace in a parameter value is discarded. Internal
+    whitespace within a parameter value is retained verbatim.</para>
+
+    <para>Any line beginning with a semicolon (<quote>;</quote>) or a hash
+    (<quote>#</quote>) character is ignored, as are lines containing only
+    whitespace.</para>
+
+    <para>Any line ending in a <quote> <literal>\</literal> </quote> is
+    continued on the next line in the customary UNIX fashion.</para>
+
+    <para>The values following the equals sign in parameters are all either a
+    string (no quotes needed) or a boolean, which may be given as yes/no, 1/0
+    or true/false. Case is not significant in boolean values, but is preserved
+    in string values. Some items such as create masks are numeric.</para>
+
+    <para>The parameter <option>include =
+    <replaceable>path</replaceable></option> allows you to include one config
+    file inside another. The file is included literally, as though typed in
+    place. Nested includes are not supported.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SECTION DESCRIPTIONS</title>
+
+    <para>Each section in the configuration file (except for the [Global]
+    section) describes a shared resource (known as a <quote>volume</quote>).
+    The section name is the name of the volume and the parameters within the
+    section define the volume attributes and options.</para>
+
+    <para>There are two special sections, [Global] and [Homes], which are
+    described under <emphasis>special sections</emphasis>. The following notes
+    apply to ordinary section descriptions.</para>
+
+    <para>A volume consists of a directory to which access is being given plus
+    a description of the access rights which are granted to the user of the
+    service. For volumes the <option>path</option> option must specify the
+    directory to share.</para>
+
+    <para>Any volume section without <option>path</option> option is
+    considered a <emphasis>vol preset</emphasis> which can be selected in
+    other volume sections via the <option>vol preset</option> option and
+    constitutes defaults for the volume. For any option specified both in a
+    preset <emphasis>and</emphasis> in a volume section the volume section
+    setting completely substitutes the preset option.</para>
+
+    <para>The access rights granted by the server are masked by the access
+    rights granted to the specified or guest UNIX user by the host system. The
+    server does not grant more access than the host system grants.</para>
+
+    <para>The following sample section defines an AFP volume. The user has
+    full access to the path <filename>/foo/bar</filename>. The share is
+    accessed via the share name <literal>baz</literal>: <programlisting> [baz]
+    path = /foo/bar </programlisting></para>
+  </refsect1>
+
+  <refsect1>
+    <title>SPECIAL SECTIONS</title>
+
+    <refsect2>
+      <title>The [Global] section</title>
+
+      <para>Parameters in this section apply to the server as a whole.
+      Parameters denoted by a (G) below are must be set in this
+      section.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>The [Homes] section</title>
+
+      <para>This section enable sharing of the UNIX server user home
+      directories. Specifying an optional <option>path</option> parameter
+      means that not the whole user home will be shared but the subdirectory
+      <option>path</option>. It is necessary to define the <option>basedir
+      regex</option> option. It should be a regex which matches the parent
+      directory of the user homes. Parameters denoted by a (H) belong to
+      volume sections. The optional parameter <option>home name</option> can
+      be used to change the AFP volume name which <emphasis>$u's
+      home</emphasis> by default. See below under VARIABLE
+      SUBSTITUTIONS.</para>
+
+      <para>The following example illustrates this. Given all user home
+      directories are stored under <filename>/home</filename>:
+      <programlisting> [Homes]
+      path = afp-data
+      basedir regex = /home</programlisting> For a user
+      <emphasis>john</emphasis> this results in an AFP home volume with a path
+      of <filename>/home/john/afp-data</filename>.</para>
+
+      <para>If <option>basedir regex</option> contains symlink, set the
+      canonicalized absolute path. When <filename>/home</filename> links to
+      <filename>/usr/home</filename>: <programlisting> [Homes]
+      basedir regex = /usr/home</programlisting></para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>PARAMETERS</title>
+
+    <para>Parameters define the specific attributes of sections.</para>
+
+    <para>Some parameters are specific to the [Global] section (e.g.,
+    <emphasis>log type</emphasis>). All others are permissible only in volume
+    sections. The letter <emphasis>G</emphasis> in parentheses indicates that
+    a parameter is specific to the [Global] section. The letter
+    <emphasis>V</emphasis> indicates that a parameter can be specified in a
+    volume specific section.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>VARIABLE SUBSTITUTIONS</title>
+
+    <para>You can use variables in volume names. The use of variables in paths
+    is not supported for now.</para>
+
+    <orderedlist>
+      <listitem>
+        <para>if you specify an unknown variable, it will not get
+        converted.</para>
+      </listitem>
+
+      <listitem>
+        <para>if you specify a known variable, but that variable doesn't have
+        a value, it will get ignored.</para>
+      </listitem>
+    </orderedlist>
+
+    <para>The variables which can be used for substitutions are:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>$b</term>
+
+        <listitem>
+          <para>basename</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$c</term>
+
+        <listitem>
+          <para>client's ip address</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$d</term>
+
+        <listitem>
+          <para>volume pathname on server</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$f</term>
+
+        <listitem>
+          <para>full name (contents of the gecos field in the passwd
+          file)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$g</term>
+
+        <listitem>
+          <para>group name</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$h</term>
+
+        <listitem>
+          <para>hostname</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$i</term>
+
+        <listitem>
+          <para>client's ip, without port</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$s</term>
+
+        <listitem>
+          <para>server name (this can be the hostname)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$u</term>
+
+        <listitem>
+          <para>user name (if guest, it is the user that guest is running
+          as)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$v</term>
+
+        <listitem>
+          <para>volume name</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>$$</term>
+
+        <listitem>
+          <para>prints dollar sign ($)</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>EXPLANATION OF GLOBAL PARAMETERS</title>
+
+    <refsect2>
+      <title>Authentication Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>ad domain = <parameter>DOMAIN</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Append @DOMAIN to username when authenticating. Useful in
+            Active Directory environments that otherwise would require the
+            user to enter the full user@domain string.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>admin auth user = <parameter>user</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifying eg "<option>admin auth user = root</option>"
+            whenever a normal user login fails, afpd will try to authenticate
+            as the specified <option>admin auth user</option>. If this
+            succeeds, a normal session is created for the original connecting
+            user. Said differently: if you know the password of <option>admin
+            auth user</option>, you can authenticate as any other user.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>k5 keytab = <replaceable>path</replaceable>
+          <type>(G)</type></term>
+
+          <term>k5 service = <replaceable>service</replaceable>
+          <type>(G)</type></term>
+
+          <term>k5 realm = <replaceable>realm</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>These are required if the server supports the Kerberos 5
+            authentication UAM.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>nt domain = <parameter>DOMAIN</parameter>
+          <type>(G)</type></term>
+
+          <term>nt separator = <parameter>SEPARATOR</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Use for eg. winbind authentication, prepends both strings
+            before the username from login and then tries to authenticate with
+            the result through the available and active UAM authentication
+            modules.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>save password = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables or disables the ability of clients to save passwords
+            locally.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>set password = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables or disables the ability of clients to change their
+            passwords via chooser or the "connect to server" dialog.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>uam list = <replaceable>uam list</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Space or comma separated list of UAMs. (The default is
+            "uams_dhx.so uams_dhx2.so").</para>
+
+            <para>The most commonly used UAMs are:</para>
+
+            <variablelist>
+              <varlistentry>
+                <term>uams_guest.so</term>
+
+                <listitem>
+                  <para>allows guest logins</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_clrtxt.so</term>
+
+                <listitem>
+                  <para>(uams_pam.so or uams_passwd.so) Allow logins with
+                  passwords transmitted in the clear. (legacy)</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_randum.so</term>
+
+                <listitem>
+                  <para>allows Random Number and Two-Way Random Number
+                  Exchange for authentication (requires a separate file
+                  containing the passwords, either @pkgconfdir@/afppasswd file or
+                  the one specified via "<option>passwd file</option>". See
+                  <citerefentry>
+                      <refentrytitle>afppasswd</refentrytitle>
+
+                      <manvolnum>1</manvolnum>
+                    </citerefentry> for details. (legacy)</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_dhx.so</term>
+
+                <listitem>
+                  <para>(uams_dhx_pam.so or uams_dhx_passwd.so) Allow
+                  Diffie-Hellman eXchange (DHX) for authentication.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uams_dhx2.so</term>
+
+                <listitem>
+                  <para>(uams_dhx2_pam.so or uams_dhx2_passwd.so) Allow
+                  Diffie-Hellman eXchange 2 (DHX2) for authentication.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>uam_gss.so</term>
+
+                <listitem>
+                  <para>Allow Kerberos V for authentication (optional)</para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>uam path = <replaceable>path</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the default path for UAMs for this server (default is
+            @libdir@/netatalk).</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Charset Options</title>
+
+      <para>With OS X Apple introduced the AFP3 protocol. One of the big
+      changes was, that AFP3 uses Unicode names encoded as Decomposed UTF-8
+      (UTF8-MAC). Previous AFP/OS versions used charsets like MacRoman,
+      MacCentralEurope, etc.</para>
+
+      <para>To be able to serve AFP3 and older clients at the same time,
+      <command>afpd</command> needs to be able to convert between UTF-8 and
+      Mac charsets. Even OS X clients partly still rely on the mac charset. As
+      there's no way, <command>afpd</command> can detect the codepage a pre
+      AFP3 client uses, you have to specify it using the <option>mac
+      charset</option> option. The default is MacRoman, which should be fine
+      for most western users.</para>
+
+      <para>As <command>afpd</command> needs to interact with UNIX operating
+      system as well, it need's to be able to convert from UTF8-MAC / Mac
+      charset to the UNIX charset. By default <command>afpd</command> uses
+      <emphasis>UTF8</emphasis>. You can set the UNIX charset using the
+      <option>unix charset</option> option. If you're using extended
+      characters in the configuration files for <command>afpd</command>, make
+      sure your terminal matches the <option>unix charset</option>.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>mac charset = <parameter>CHARSET</parameter>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the Mac clients charset, e.g.
+            <emphasis>MAC_ROMAN</emphasis>. This is used to convert strings
+            and filenames to the clients codepage for OS9 and Classic, i.e.
+            for authentication and AFP messages (SIGUSR2 messaging). This will
+            also be the default for the volumes <option>mac charset</option>.
+            Defaults to <emphasis>MAC_ROMAN</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>unix charset = <parameter>CHARSET</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the servers unix charset, e.g.
+            <emphasis>ISO-8859-15</emphasis> or <emphasis>EUC-JP</emphasis>.
+            This is used to convert strings to/from the systems locale, e.g.
+            for authentication, server messages and volume names. If
+            <emphasis>LOCALE</emphasis> is set, the systems locale is used.
+            Defaults to <emphasis>UTF8</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol charset = <parameter>CHARSET</parameter>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the encoding of the volumes filesystem. By
+            default, it is the same as <option>unix charset</option>.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Password Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>passwd file = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to the Randnum UAM passwd file for this server
+            (default is @pkgconfdir@/afppasswd).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>passwd minlen = <parameter>number</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the minimum password length, if supported by the
+            UAM</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Network Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>advertise ssh = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows old Mac OS X clients (10.3.3-10.4) to automagically
+            establish a tunneled AFP connection through SSH. If this option is
+            set, the server's answers to client's FPGetSrvrInfo requests
+            contain an additional entry. It depends on both client's settings
+            and a correctly configured and running <citerefentry>
+                <refentrytitle>sshd</refentrytitle>
+
+                <manvolnum>8</manvolnum>
+              </citerefentry> on the server to let things work.</para>
+
+            <note>
+              <para>Setting this option is not recommended since globally
+              encrypting AFP connections via SSH will increase the server's
+              load significantly. On the other hand, Apple's client side
+              implementation of this feature in MacOS X versions prior to
+              10.3.4 contained a security flaw.</para>
+            </note>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp interfaces = <replaceable>name [name ...]</replaceable>
+            <type>(G)</type></term>
+          <listitem>
+            <para>Specifies the network interfaces that the server should
+            listens on. The default is advertise the first IP address of the
+            system, but to listen for any incoming request.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp listen = <replaceable>ip address[:port] [ip address[:port]
+          ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address that the server should advertise
+            <emphasis role="bold">and</emphasis> listens to. The default is
+            advertise the first IP address of the system, but to listen for
+            any incoming request. The network address may be specified either
+            in dotted-decimal format for IPv4 or in hexadecimal format for
+            IPv6.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp port = <replaceable>port number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows a different TCP port to be used for AFP. The default
+            is 548. Also sets the default port applied when none specified in
+            an <option>afp listen</option> option.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid listen = <replaceable>ip address[:port] [ip
+          address[:port] ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address that the CNID server should listen
+            on. The default is <emphasis
+            role="bold">localhost:4700</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>disconnect time = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Keep disconnected AFP sessions for
+            <parameter>number</parameter> hours before dropping them. Default
+            is 24 hours.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>dsireadbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Scale factor that determines the size of the DSI/TCP
+            readahead buffer, default is 12. This is multiplies with the DSI
+            server quantum (default ~300k) to give the size of the buffer.
+            Increasing this value might increase throughput in fast local
+            networks for volume to volume copies. <emphasis>Note</emphasis>:
+            This buffer is allocated per afpd child process, so specifying
+            large values will eat up large amount of memory (buffer size *
+            number of clients).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fqdn = <replaceable>name:port</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies a fully-qualified domain name, with an optional
+            port. This is discarded if the server cannot resolve it. This
+            option is not honored by AppleShare clients &lt;= 3.8.3. This
+            option is disabled by default. Use with caution as this will
+            involve a second name resolution step on the client side. Also
+            note that afpd will advertise this name:port combination but not
+            automatically listen to it.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hostname = <replaceable>name</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Use this instead of the result from calling hostname for
+            determining which IP address to advertise, therefore the hostname
+            is resolved to an IP which is the advertised. This is NOT used for
+            listening and it is also overwritten by <option>afp
+            listen</option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>max connections = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the maximum number of clients that can simultaneously
+            connect to the server (default is 200).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>server quantum = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>This specifies the DSI server quantum. The default value is
+            1 MB. The maximum value is 0xFFFFFFFFF, the minimum is 32000. If
+            you specify a value that is out of range, the default value will
+            be set. Do not change this value unless you're absolutely sure,
+            what you're doing</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>sleep time = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Keep sleeping AFP sessions for <parameter>number</parameter>
+            hours before disconnecting clients in sleep mode. Default is 10
+            hours.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>tcprcvbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Try to set TCP receive buffer using setsockpt(). Often OSes
+            impose restrictions on the applications ability to set this
+            value.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>tcpsndbuf = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Try to set TCP send buffer using setsockpt(). Often OSes
+            impose restrictions on the applications ability to set this
+            value.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>use sendfile = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to use sendfile<indexterm>
+                <primary>sendfile</primary>
+              </indexterm> syscall for sending file data to clients.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>zeroconf = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to use automatic Zeroconf<indexterm>
+                <primary>Zeroconf</primary>
+
+                <secondary>Bonjour</secondary>
+              </indexterm> service registration if Avahi or mDNSResponder were
+            compiled in.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Miscellaneous Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>admin group = <replaceable>group</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Allows users of a certain group to be seen as the superuser
+            when they log in. This option is disabled by default.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afp read locks = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to apply locks to the byte region read in FPRead
+            calls. The AFP spec mandates this, but it's not really in line
+            with UNIX semantics and is a performance hug.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>afpstats = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to provide AFP runtime statistics (connected
+            users, open volumes) via dbus.</para>            
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>basedir regex = <replaceable>regex</replaceable>
+          <type>(H)</type></term>
+
+          <listitem>
+            <para>Regular expression which matches the parent directory of the
+            user homes. If <option>basedir regex</option> contains symlink,
+            you must set the canonicalized absolute path. In the simple case
+            this is just a path ie <option>basedir regex =
+            /home</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>close vol = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to close volumes possibly opened by clients when
+            they're removed from the configuration and the configuration is
+            reloaded.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid server = <replaceable>ipaddress[:port]</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Specifies the IP address and port of a cnid_metad server,
+            required for CNID dbd backend. Defaults to localhost:4700. The
+            network address may be specified either in dotted-decimal format
+            for IPv4 or in hexadecimal format for IPv6.-</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>dircachesize = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Maximum possible entries in the directory cache. The cache
+            stores directories and files. It is used to cache the full path to
+            directories and CNIDs which considerably speeds up directory
+            enumeration.</para>
+
+            <para>Default size is 8192, maximum size is 131072. Given value is
+            rounded up to nearest power of 2. Each entry takes about 100
+            bytes, which is not much, but remember that every afpd child
+            process for every connected user has its cache.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>extmap file = <parameter>path</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the path to the file which defines file extension
+            type/creator mappings. (default is @pkgconfdir@/extmap.conf).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>guest account = <replaceable>name</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the user that guests should use (default is
+            "nobody"). The name should be quoted.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>home name = <replaceable>name</replaceable>
+          <type>(H)</type></term>
+
+          <listitem>
+            <para>AFP user home volume name. The default is <emphasis>user's
+            home</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>login message = <replaceable>message</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Sets a message to be displayed when clients logon to the
+            server. The message should be in <option>unix charset</option> and
+            should be quoted. Extended characters are allowed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>mimic model = <replaceable>model</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies the icon model that appears on clients. Defaults
+            to off. Note that afpd must support Zeroconf.
+            Examples: RackMac (same as Xserve), PowerBook, PowerMac,
+            Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro,
+            AppleTV1,1, AirPort.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>signature = &lt;text&gt; <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify a server signature. The maximum length is 16
+            characters. This option is useful for clustered environments, to
+            provide fault isolation etc. By default, afpd generate signature
+            and saving it to
+            <filename>@localstatedir@/netatalk/afp_signature.conf</filename>
+            automatically (based on random number). See also
+            asip-status.pl(1).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>solaris share reservations =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Use share reservations on Solaris. Solaris CIFS server uses
+            this too, so this makes a lock coherent multi protocol
+            server.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol dbpath = <replaceable>path</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the database information to be stored in path. You have
+            to specify a writable location, even if the volume is read only.
+            The default is
+            <filename>@localstatedir@/netatalk/CNID/</filename>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>volnamelen = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Max length of UTF8-MAC volume name for Mac OS X. Note that
+            Hangul is especially sensitive to this.</para>
+
+            <para><programlisting> 73: limit of Mac OS X 10.1 80: limit of Mac
+            OS X 10.4/10.5 (default) 255: limit of recent Mac OS
+            X</programlisting> Mac OS 9 and earlier are not influenced by
+            this, because Maccharset volume name is always limited to 27
+            bytes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol preset = <replaceable>name</replaceable>
+          <type>(G)/(V)</type></term>
+
+          <listitem>
+            <para>Use section <option>name</option> as option preset for all
+            volumes (when set in the [Global] section) or for one volume (when
+            set in that volume's section).</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Logging Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term>log file = <replaceable>logfile</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>If not specified Netatalk logs to syslogs daemon facility.
+            Otherwise it logs to <option>logfile</option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>log level = <replaceable>type:level [type:level
+          ...]</replaceable> <type>(G)</type></term>
+
+          <term>log level = <replaceable>type:level,[type:level,
+          ...]</replaceable> <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify that any message of a loglevel up to the given
+            <option>log level</option> should be logged.</para>
+
+            <para>By default afpd logs to syslog with a default logging setup
+            equivalent to <option>default:note</option></para>
+
+            <para>logtypes: default, afpdaemon, logger, uamsdaemon</para>
+
+            <para>loglevels: severe, error, warn, note, info, debug, debug6,
+            debug7, debug8, debug9, maxdebug</para>
+
+            <note>
+              <para>Both logtype and loglevels are case insensitive.</para>
+            </note>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2 id="fceconf">
+      <title>Filesystem Change Events (FCE<indexterm>
+          <primary>FCE</primary>
+        </indexterm>)</title>
+
+      <para>Netatalk includes a nifty filesystem change event mechanism where
+      afpd processes notify interested listeners about certain filesystem
+      event by UDP network datagrams.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>fce listener = <replaceable>host[:port]</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Enables sending FCE events to the specified
+            <parameter>host</parameter>, default <parameter>port</parameter>
+            is 12250 if not specified. Specifying multiple listeners is done
+            by having this option once for each of them.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce events =
+          <replaceable>fmod,fdel,ddel,fcre,dcre,tmsz</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specifies which FCE events are active, default is
+            <parameter>fmod,fdel,ddel,fcre,dcre</parameter>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce coalesce = <replaceable>all|delete|create</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Coalesce FCE events.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>fce holdfmod = <replaceable>seconds</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>This determines the time delay in seconds which is always
+            waited if another file modification for the same file is done by a
+            client before sending an FCE file modification event (fmod). For
+            example saving a file in Photoshop would generate multiple events
+            by itself because the application is opening, modifying and
+            closing a file multiple times for every "save". Default: 60
+            seconds.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Debug Parameters</title>
+
+      <para>These options are useful for debugging only.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>tickleval = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Sets the tickle timeout interval (in seconds). Defaults to
+            30.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>timeout = <replaceable>number</replaceable>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Specify the number of tickles to send before timing out a
+            connection. The default is 4, therefore a connection will timeout
+            after 2 minutes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>client polling = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>With this option enabled, afpd won't advertise that it is
+            capable of server notifications, so that connected clients poll
+            the server every 10 seconds to detect changes in opened server
+            windows. <emphasis>Note</emphasis>: Depending on the number of
+            simultaneously connected clients and the network's speed, this can
+            lead to a significant higher load on your network!</para>
+
+            <para>Do not use this option any longer as present Netatalk
+            correctly supports server notifications, allowing connected
+            clients to update folder listings in case another client changed
+            the contents.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2 id="acl_options">
+      <title>Options for ACL handling</title>
+
+      <para>By default, the effective permission of the authenticated user are
+      only mapped to the mentioned UARights permission structure, not the UNIX
+      mode. You can adjust this behaviour with the configuration option
+      <option>mac acls</option>:</para>
+
+      <variablelist id="mac_acls">
+        <varlistentry>
+          <term>map acls = <parameter>none|rights|mode</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para><variablelist>
+                <varlistentry>
+                  <term>none</term>
+
+                  <listitem>
+                    <para>no mapping of ACLs </para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>rights</term>
+
+                  <listitem>
+                    <para>effective permissions are mapped to UARights
+                    structure. This is the default.</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>mode</term>
+
+                  <listitem>
+                    <para>ACLs are additionally mapped to the UNIX mode of the
+                    filesystem object.</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>If you want to be able to display ACLs on the client, you must
+      setup both client and server as part on a authentication domain
+      (directory service, eg LDAP, Open Directory, Active Directory). The
+      reason is, in OS X ACLs are bound to UUIDs, not just uid's or gid's.
+      Therefor Netatalk must be able to map every filesystem uid and gid to a
+      UUID so that it can return the server side ACLs which are bound to UNIX
+      uid and gid mapped to OS X UUIDs.</para>
+
+      <para>Netatalk can query a directory server using LDAP queries. Either
+      the directory server already provides an UUID attribute for user and
+      groups (Active Directory, Open Directory) or you reuse an unused
+      attribute (or add a new one) to you directory server (eg
+      OpenLDAP).</para>
+
+      <para>The following LDAP options must be configured for Netatalk:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>ldap auth method = <parameter>none|simple|sasl</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Authentication method: <option>none | simple |
+            sasl</option></para>
+
+            <para><variablelist>
+                <varlistentry>
+                  <term>none</term>
+
+                  <listitem>
+                    <para>anonymous LDAP bind</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>simple</term>
+
+                  <listitem>
+                    <para>simple LDAP bind</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>sasl</term>
+
+                  <listitem>
+                    <para>SASL. Not yet supported !</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap auth dn = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Distinguished Name of the user for simple bind.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap auth pw = <parameter>password</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Distinguished Name of the user for simple bind.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap server = <parameter>host</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name or IP address of your LDAP Server. This is only needed
+            for explicit ACL support in order to be able to query LDAP for
+            UUIDs.</para>
+
+            <para>You can use <citerefentry>
+                <refentrytitle>afpldaptest</refentrytitle>
+
+                <manvolnum>1</manvolnum>
+              </citerefentry> to syntactically check your config.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap userbase = <parameter>base dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>DN of the user container in LDAP.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap userscope = <parameter>scope</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Search scope for user search: <option>base | one |
+            sub</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap groupbase = <parameter>base dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>DN of the group container in LDAP.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap groupscope = <parameter>scope</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Search scope for user search: <option>base | one |
+            sub</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the UUIDs.</para>
+
+            <para>Note: this is used both for users and groups.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap name attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the users short name.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid string = <parameter>STRING</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Format of the uuid string in the directory. A series of x
+            and -, where every x denotes a value 0-9a-f and every - is a
+            separator.</para>
+
+            <para>Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap uuid encoding = <parameter>string | ms-guid (default:
+          string)</parameter> <type>(G)</type></term>
+
+          <listitem>
+            <para>Format of the UUID of the LDAP attribute, allows usage of
+            the binary objectGUID fields from Active Directory. If left
+            unspecified, string is the default, which passes through the ASCII
+            UUID returned by most other LDAP stores. If set to ms-guid, the
+            internal UUID representation is converted to and from the binary
+            format used in the objectGUID attribute found on objects in Active
+            Directory when interacting with the server.</para>
+
+            <para><variablelist>
+                <varlistentry>
+                  <term>string</term>
+
+                  <listitem>
+                    <para>UUID is a string, use with eg OpenDirectory.</para>
+                  </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                  <term>ms-guid</term>
+
+                  <listitem>
+                    <para>Binary objectGUID from Active Directory</para>
+                  </listitem>
+                </varlistentry>
+              </variablelist></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ldap group attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the groups short
+            name.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>EXPLANATION OF VOLUME PARAMETERS</title>
+
+    <refsect2>
+      <title>Parameters</title>
+
+      <para>The section name defines the volume name.
+      No two volumes may have the same
+      name. The volume name cannot contain the <keycode>':'</keycode>
+      character. The volume name is mangled if it is very long. Mac charset
+      volume name is limited to 27 characters. UTF8-MAC volume name is limited
+      to volnamelen parameter.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>path = <replaceable>PATH</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>The path name must be a fully qualified path name.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>appledouble = <replaceable>ea|v2</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Specify the format of the metadata files, which are used for
+            saving Mac resource fork as well. Earlier versions used
+            AppleDouble v2, the new default format is <emphasis
+            role="bold">ea</emphasis>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>vol size limit = <replaceable>size in MiB</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Useful for Time Machine: limits the reported volume size,
+            thus preventing Time Machine from using the whole real disk space
+            for backup. Example: "vol size limit = 1000" would limit the
+            reported disk space to 1 GB. <emphasis role="bold">IMPORTANT:
+            </emphasis> This is an approximated calculation taking into
+            account the contents of Time Machine sparsebundle images. Therefor
+            you MUST NOT use this volume to store other content when using
+            this option, because it would NOT be accounted. The calculation
+            works by reading the band size from the Info.plist XML file of the
+            sparsebundle, reading the bands/ directory counting the number of
+            band files, and then multiplying one with the other.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>valid users = <replaceable>user @group</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>The allow option allows the users and groups that access a
+            share to be specified. Users and groups are specified, delimited
+            by spaces or commas. Groups are designated by a @ prefix. Names
+            may be quoted in order to allow for spaces in names. Example:
+            <programlisting>valid users = user "user 2" @group “@group 2"</programlisting></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>invalid users = <replaceable>users/groups</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>The deny option specifies users and groups who are not
+            allowed access to the share. It follows the same format as the
+            "valid users" option.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hosts allow = <replaceable>IP host address/IP netmask bits [
+          ... ]</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>Only listed hosts and networks are allowed, all others are
+            rejected. The network address may be specified either in
+            dotted-decimal format for IPv4 or in hexadecimal format for
+            IPv6.</para>
+
+            <para>Example: hosts allow = 10.1.0.0/16 10.2.1.100
+            2001:0db8:1234::/48</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>hosts deny = <replaceable>IP host address/IP netmask bits [
+          ... ]</replaceable> <type>(V)</type></term>
+
+          <listitem>
+            <para>Listed hosts and nets are rejected, all others are
+            allowed.</para>
+
+            <para>Example: hosts deny = 192.168.100/24 10.1.1.1
+            2001:db8::1428:57ab</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid scheme = <replaceable>backend</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>set the CNID backend to be used for the volume, default is
+            [@DEFAULT_CNID_SCHEME@] available schemes:
+            [@compiled_backends@]</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>ea = <replaceable>none|auto|sys|ad</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Specify how Extended Attributes<indexterm>
+                <primary>Extended Attributes</primary>
+              </indexterm> are stored. <option>auto</option> is the
+            default.</para>
+
+            <variablelist>
+              <varlistentry>
+                <term>auto</term>
+
+                <listitem>
+                  <para>Try <option>sys</option> (by setting an EA on the
+                  shared directory itself), fallback to <option>ad</option>.
+                  Requires writable volume for performing test. "<option>read
+                  only = yes</option>" overwrites <option>auto</option> with
+                  <option>none</option>. Use explicit "<option>ea =
+                  sys|ad</option>" for read-only volumes where
+                  appropriate.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>sys</term>
+
+                <listitem>
+                  <para>Use filesystem Extended Attributes.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>ad</term>
+
+                <listitem>
+                  <para>Use files in <emphasis>.AppleDouble</emphasis>
+                  directories.</para>
+                </listitem>
+              </varlistentry>
+
+              <varlistentry>
+                <term>none</term>
+
+                <listitem>
+                  <para>No Extended Attributes support.</para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>mac charset = <replaceable>CHARSET</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>specifies the Mac client charset for this Volume, e.g.
+            <emphasis>MAC_ROMAN</emphasis>, <emphasis>MAC_CYRILLIC</emphasis>.
+            If not specified the global setting is applied. This setting is
+            only required if you need volumes, where the Mac charset differs
+            from the one globally set in the [Global] section.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>casefold = <option>option</option> <type>(V)</type></term>
+
+          <listitem>
+            <para>The casefold option handles, if the case of filenames should
+            be changed. The available options are:</para>
+
+            <para><option>tolower</option> - Lowercases names in both
+            directions.</para>
+
+            <para><option>toupper</option> - Uppercases names in both
+            directions.</para>
+
+            <para><option>xlatelower</option> - Client sees lowercase, server
+            sees uppercase.</para>
+
+            <para><option>xlateupper</option> - Client sees uppercase, server
+            sees lowercase.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>password = <replaceable>password</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>This option allows you to set a volume password, which can
+            be a maximum of 8 characters long (using ASCII strongly
+            recommended at the time of this writing).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>file perm = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <term>directory perm = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Add(or) with the client requested permissions: <option>file
+            perm</option> is for files only, <option>directory perm</option>
+            is for directories only. Don't use with "<option>unix priv =
+            no</option>".</para>
+
+            <example>
+              <title>Volume for a collaborative workgroup</title>
+
+              <para><programlisting>file perm = 0660 directory perm =
+              0770</programlisting></para>
+            </example>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>umask = <replaceable>mode</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>set perm mask. Don't use with "<option>unix priv =
+            no</option>".</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>preexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run when the volume is mounted, ignored for
+            user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>postexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run when the volume is closed, ignored for
+            user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root preexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run as root when the volume is mounted,
+            ignored for user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root postexec = <replaceable>command</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>command to be run as root when the volume is closed, ignored
+            for user defined volumes</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>rolist = <option>users/groups</option> <type>(V)</type></term>
+
+          <listitem>
+            <para>Allows certain users and groups to have read-only access to
+            a share. This follows the allow option format.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>rwlist = <replaceable>users/groups</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>Allows certain users and groups to have read/write access to
+            a share. This follows the allow option format.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>veto files = <replaceable>vetoed names</replaceable>
+          <type>(V)</type></term>
+
+          <listitem>
+            <para>hide files and directories,where the path matches one of the
+            '/' delimited vetoed names. The veto string must always be
+            terminated with a '/', eg. "veto1/", "veto1/veto2/".</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Volume options</title>
+
+      <para>Boolean volume options.</para>
+
+      <variablelist>
+        <varlistentry>
+          <term>acls = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to flag volumes as supporting ACLs. If ACL support
+            is compiled in, this is yes by default.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>cnid dev = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to use the device number in the CNID backends. Helps
+            when the device number is not constant across a reboot, eg
+            cluster, ...</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>convert appledouble = <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether automatic conversion from <option>appledouble =
+            v2</option> to <option>appledouble = ea</option> is performed when
+            accessing filesystems from clients. This is generally useful, but
+            costs some performance. It's recommendable to run
+            <command>dbd</command> on volumes and do the conversion with that.
+            Then this option can be set to no.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>follow symlinks = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>The default setting is false thus symlinks are not followed
+            on the server. This is the same behaviour as OS X's AFP server.
+            Setting the option to true causes afpd to follow symlinks on the
+            server. symlinks may point outside of the AFP volume, currently
+            afpd doesn't do any checks for "wide symlinks".</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>invisible dots = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>make dot files invisible. WARNING: enabling this option will
+              lead to unwanted sideeffects were OS X applications when saving
+              files to a temporary file starting with a dot first, then renaming
+              the temp file to its final name, result in the saved file being
+              invisible. The only thing this option is useful for is making
+              files that start with a dot invisible on Mac OS 9. It's
+              completely useless on Mac OS X, as both in Finder and in Terminal
+              files starting with a dot are hidden anyway.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>network ids = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether the server support network ids. Setting this to
+            <emphasis>no</emphasis> will result in the client not using ACL
+            AFP functions.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>preexec close = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>A non-zero return code from preexec close the volume being
+            immediately, preventing clients to mount/see the volume in
+            question.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>read only = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Specifies the share as being read only for all users.
+            Overwrites <option>ea = auto</option> with <option>ea =
+            none</option></para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>root preexec close= <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>A non-zero return code from root_preexec closes the volume
+            immediately, preventing clients to mount/see the volume in
+            question.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>search db = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Use fast CNID database namesearch instead of slow recursive
+            filesystem search. Relies on a consistent CNID database, ie Samba
+            or local filesystem access lead to inaccurate or wrong results.
+            Works only for "dbd" CNID db volumes.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>stat vol = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to stat volume path when enumerating volumes list,
+            useful for automounting or volumes created by a preexec
+            script.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>time machine = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to enable Time Machine support for this
+            volume.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>unix priv = <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>Whether to use AFP3 UNIX privileges. This should be set for
+            OS X clients. See also: <option>file perm</option>,
+            <option>directory perm</option> and <option>umask</option>.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>CNID backends</title>
+
+    <para>The AFP protocol mostly refers to files and directories by ID and
+    not by name. Netatalk needs a way to store these ID's in a persistent way,
+    to achieve this several different CNID backends are available. The CNID
+    Databases are by default located in the
+    <filename>@localstatedir@/netatalk/CNID/(volumename)/.AppleDB/</filename>
+    directory.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>cdb</term>
+
+        <listitem>
+          <para>"Concurrent database", backend is based on Oracle Berkley DB.
+          With this backend several <command>afpd</command> daemons access the
+          CNID database directly. Berkeley DB locking is used to synchronize
+          access, if more than one <command>afpd</command> process is active
+          for a volume. The drawback is, that the crash of a single
+          <command>afpd</command> process might corrupt the database.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>dbd</term>
+
+        <listitem>
+          <para>Access to the CNID database is restricted to the
+          <command>cnid_metad</command> daemon process.
+          <command>afpd</command> processes communicate with the daemon for
+          database reads and updates. If built with Berkeley DB transactions
+          the probability for database corruption is practically zero, but
+          performance can be slower than with <option>cdb</option></para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>last</term>
+
+        <listitem>
+          <para>This backend is an exception, in terms of ID persistency. ID's
+          are only valid for the current session. This is basically what
+          <command>afpd</command> did in the 1.5 (and 1.6) versions. This
+          backend is still available, as it is useful for e.g. sharing cdroms.
+          Starting with Netatalk 3.0, it becomes the <emphasis>read only
+          mode</emphasis> automatically.</para>
+
+          <para><emphasis role="bold">Warning</emphasis>: It is
+          <emphasis>NOT</emphasis> recommended to use this backend for volumes
+          anymore, as <command>afpd</command> now relies heavily on a
+          persistent ID database. Aliases will likely not work and filename
+          mangling is not supported.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Even though <command>./configure --help</command> might show that
+    there are other CNID backends available, be warned those are likely broken
+    or mainly used for testing. Don't use them unless you know what you're
+    doing, they may be removed without further notice from future
+    versions.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Charset options</title>
+
+    <para>With OS X Apple introduced the AFP3 protocol. One of the most
+    important changes was that AFP3 uses unicode names encoded as UTF-8
+    decomposed. Previous AFP/OS versions used codepages, like MacRoman,
+    MacCentralEurope, etc.</para>
+
+    <para><command>afpd</command> needs a way to preserve extended Macintosh
+    characters, or characters illegal in unix filenames, when saving files on
+    a unix filesystem. Earlier versions used the the so called CAP encoding.
+    An extended character (&gt;0x7F) would be converted to a :xx sequence,
+    e.g. the Apple Logo (MacRoman: 0xF0) was saved as <literal>:f0</literal>.
+    Some special characters will be converted as to :xx notation as well.
+    '<keycode>/</keycode>' will be encoded to <literal>:2f</literal>, if
+    <option>usedots</option> is not specified, a leading dot
+    '<keycode>.</keycode>' will be encoded as <literal>:2e</literal>.</para>
+
+    <para>This version now uses UTF-8 as the default encoding for names.
+    '<keycode>/</keycode>' will be converted to '<keycode>:</keycode>'.</para>
+
+    <para>The <option>vol charset</option> option will allow you to select
+    another volume encoding. E.g. for western users another useful setting
+    could be vol charset ISO-8859-15. <command>afpd</command> will accept any
+    <citerefentry>
+        <refentrytitle><command>iconv</command></refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry> provided charset. If a character cannot be converted
+    from the <option>mac charset</option> to the selected <option>vol
+    charset</option>, afpd will save it as a CAP encoded character. For AFP3
+    clients, <command>afpd</command> will convert the UTF-8<indexterm>
+        <primary>UTF8</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>UTF8-MAC</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>ISO-8859-15</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm><indexterm>
+        <primary>ISO-8859-1</primary>
+
+        <secondary>afpd's vol charset setting</secondary>
+      </indexterm> character to <option>mac charset</option> first. If this
+    conversion fails, you'll receive a -50 error on the mac.</para>
+
+    <para><emphasis>Note</emphasis>: Whenever you can, please stick with the
+    default UTF-8 volume format.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afppasswd</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_signature.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>extmap.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/afp_signature.conf.5.xml b/doc/manpages/man5/afp_signature.conf.5.xml
new file mode 100644 (file)
index 0000000..287b6d7
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp_signature.conf.5">
+
+  <refmeta>
+    <refentrytitle>afp_signature.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp_signature.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to specify server
+        signature<indexterm>
+        <primary>afp_signature.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>@localstatedir@/netatalk/afp_signature.conf</filename> is the
+    configuration file used by <command>afpd</command> to specify
+    server signature automagically. The configuration lines are
+    composed like:</para>
+
+    <para><replaceable>"server name"</replaceable>
+    <replaceable>hexa-string</replaceable></para>
+
+    <para>The first field is server name. Server names must be quoted
+    if they contain spaces. The second field is the hexadecimal string
+    of 32 characters for 16-bytes server signature.</para>
+    <para>The leading spaces and tabs are ignored. Blank lines are ignored.
+    The lines prefixed with # are ignored. The illegal lines are ignored.
+    </para>
+
+    <note>
+        <para>Server Signature is unique 16-bytes identifier used to
+        prevent logging on to the same server twice. </para>
+        <para>Netatalk 2.0 and earlier generated server signature by using
+        gethostid(). There was a problem that another servers have the same
+        signature because the hostid is not unique enough.</para>
+        <para>Current netatalk generates the signature from random numbers and
+        saves it into afp_signature.conf. When starting next time, it
+        is read from this file. </para>
+        <para>This file should not be thoughtlessly edited and be copied
+        onto another server. If it wants to set the signature intentionally,
+        use the option "signature ="  in afp.conf. In this case,
+        afp_signature.conf is not used.</para>
+    </note>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>afp_signature.conf</title>
+
+      <programlisting># This is a comment.
+"My Server" 74A0BB94EC8C13988B2E75042347E528</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>asip-status.pl</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/afp_voluuid.conf.5.xml b/doc/manpages/man5/afp_voluuid.conf.5.xml
new file mode 100644 (file)
index 0000000..0956b47
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afp_voluuid.conf.5">
+
+  <refmeta>
+    <refentrytitle>afp_voluuid.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afp_voluuid.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to specify UUID
+        for Time Machine volume<indexterm>
+        <primary>afp_voluuid.conf</primary>
+      </indexterm></refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>@localstatedir@/netatalk/afp_voluuid.conf</filename> is the
+    configuration file used by <command>afpd</command> to specify
+    UUID of Time Machine volume automagically. The configuration
+    lines are composed like:</para>
+
+    <para><replaceable>"volume name"</replaceable>
+    <replaceable>uuid-string</replaceable></para>
+
+    <para>The first field is volume name. Volume names must be quoted
+    if they contain spaces. The second field is the 36 character
+    hexadecimal ASCII string representation of a UUID.</para>
+    <para>The leading spaces and tabs are ignored. Blank lines are ignored.
+    The lines prefixed with # are ignored. The illegal lines are ignored.
+    </para>
+
+    <note>
+        <para>This UUID is advertised by Zeroconf in order to provide
+        robust disambiguation of Time Machine volume.</para>
+        <para>The afpd generates the UUID from random numbers and saves it
+        into afp_voluuid.conf, only when setting "time machine = yes" option
+        in afp.conf.</para>
+        <para>This file should not be thoughtlessly edited and be copied
+        onto another server.</para>
+    </note>
+
+    <para></para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>afp_voluuid.conf three TM volumes on one netatalk</title>
+
+      <programlisting># This is a comment.
+"Backup for John Smith" 1573974F-0ABD-69CC-C40A-8519B681A0E1
+"bob" 39A487F4-55AA-8240-E584-69AA01800FE9
+mary 6331E2D1-446C-B68C-3066-D685AADBE911</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>avahi-daemon</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>mDNSResponder</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man5/extmap.conf.5.xml b/doc/manpages/man5/extmap.conf.5.xml
new file mode 100644 (file)
index 0000000..4265928
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="extmap.conf.5">
+  <refmeta>
+    <refentrytitle>extmap.conf</refentrytitle>
+
+    <manvolnum>5</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>extmap.conf</refname>
+
+    <refpurpose>Configuration file used by afpd(8) to
+    specify file name extension mappings.<indexterm>
+        <primary>extmap.conf</primary>
+      </indexterm>
+      </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id="synopsis">
+    <cmdsynopsis>
+      <command>@pkgconfdir@/extmap.conf<indexterm><primary>extmap.conf</primary></indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+    <filename>@pkgconfdir@/extmap.conf</filename> is the
+    configuration file used by <command>afpd</command> to
+    specify file name extension mappings.</para>
+
+    <para>The configuration lines are composed like:</para>
+
+    <para><filename>.extension</filename> <replaceable>[ type [
+    creator ] ]</replaceable></para>
+
+    <para>Any line beginning with a hash (“#”) character is ignored.
+    The leading-dot lines specify file name extension mappings.
+    The extension '.' sets the default creator and type for otherwise
+    untyped Unix files.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Extension is jpg. Type is "JPEG". Creator is "ogle".</title>
+
+            <programlisting>.jpg "JPEG" "ogle"</programlisting>
+          </example>
+
+    <example>
+      <title>Extension is lzh. Type is "LHA ". Creator is not defined.</title>
+
+            <programlisting>.lzh "LHA "</programlisting>
+          </example>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para><citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/.gitignore b/doc/manpages/man8/.gitignore
new file mode 100644 (file)
index 0000000..c756d8f
--- /dev/null
@@ -0,0 +1 @@
+*\.8
diff --git a/doc/manpages/man8/Makefile.am b/doc/manpages/man8/Makefile.am
new file mode 100644 (file)
index 0000000..3f23a78
--- /dev/null
@@ -0,0 +1,26 @@
+XSLTPROC=@XSLTPROC@
+XSLTPROC_FLAGS=@XSLTPROC_FLAGS@
+MAN_STYLESHEET=$(top_srcdir)/doc/man.xsl
+CLEANFILES =
+
+MAN_MANPAGES = \
+       afpd.8 \
+       cnid_dbd.8 \
+       cnid_metad.8 \
+       netatalk.8
+
+EXTRA_DIST = \
+       afpd.8.xml \
+       cnid_dbd.8.xml \
+       cnid_metad.8.xml \
+       netatalk.8.xml
+
+if HAVE_XSLTPROC
+CLEANFILES += $(MAN_MANPAGES)
+
+%.8 : $(MAN_STYLESHEET) %.8.xml
+       @xsltproc $(MAN_STYLESHEET) $<
+       @cp $@ $(top_builddir)/man/man8/$@.in
+
+html-local: $(MAN_MANPAGES)
+endif
\ No newline at end of file
diff --git a/doc/manpages/man8/afpd.8.xml b/doc/manpages/man8/afpd.8.xml
new file mode 100644 (file)
index 0000000..fff2758
--- /dev/null
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="afpd.8">
+  <refmeta>
+    <refentrytitle>afpd</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">19 Jan 2013</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>afpd</refname>
+
+    <refpurpose>Apple Filing Protocol daemon</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>afpd<indexterm>
+          <primary>afpd</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+
+      <arg choice="opt">-F <replaceable>configfile</replaceable></arg>
+
+      <sbr />
+
+      <command>afpd<indexterm>
+          <primary>afpd</primary>
+        </indexterm></command>
+
+      <group choice="plain">
+        <arg choice="plain">-v</arg>
+        <arg choice="plain">-V</arg>
+        <arg choice="plain">-h</arg>
+      </group>
+
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>afpd</command> provides an Apple Filing Protocol (AFP)
+    interface to the Unix file system. It is normally started at boot time
+    by <command>netatalk</command>(8).</para>
+
+    <para><filename>@pkgconfdir@/afp.conf</filename> is the configuration file
+    used by <command>afpd</command> to determine the behavior and
+    configuration of a file server.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-d</term>
+
+        <listitem>
+          <para>Specifies that the daemon should not fork.</para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term>-v</term>
+
+        <listitem>
+          <para>Print version information and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-V</term>
+
+        <listitem>
+          <para>Print verbose information and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-h</term>
+
+        <listitem>
+          <para>Print help and exit.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-F <replaceable>configfile</replaceable></term>
+
+        <listitem>
+          <para>Specifies the configuration file to use. (Defaults to
+          <filename>@pkgconfdir@/afp.conf</filename>.)</para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SIGNALS</title>
+
+    <para>To shut down a user's <command>afpd</command> process it is
+      recommended that <command>SIGKILL (-9)</command>
+      <emphasis>NOT</emphasis> be used, except as a last resort, as this
+      may leave the CNID database in an inconsistent state. The safe way
+      to terminate an <command>afpd</command> is to send it a
+      <command>SIGTERM (-15)</command> signal and wait for it to die on
+      its own.</para>
+    <para>SIGTERM and SIGUSR1 signals that are sent to the main <command>afpd</command> process
+    are propagated to the children, so all will be affected.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>SIGTERM</term>
+        <listitem>
+          <para>Clean exit. Propagates from master to childs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGQUIT</term>
+        <listitem>
+          <para>Send this to the master <command>afpd</command>, it will
+          exit leaving all children running! Can be used to implement
+          AFP service without downtime.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGHUP</term>
+        <listitem>
+          <para>Sending a <command>SIGHUP</command> to afpd will cause it to
+          reload its configuration files.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGINT</term>
+        <listitem>
+          <para>Sending a <command>SIGINT</command> to a child
+          <command>afpd</command> enables <emphasis>max_debug</emphasis>
+          logging for this process. The log is sent to the file
+          <filename>/tmp/afpd.PID.XXXXXX</filename>. Sending another
+          <command>SIGINT</command> will revert to the original log settings.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGUSR1</term>
+        <listitem>
+          <para>The <command>afpd</command> process will send the message "The
+          server is going down for maintenance." to the client and shut itself
+          down in 5 minutes. New connections are not allowed. If this is sent
+          to a child afpd, the other children are not affected. However, the
+          main process will still exit, disabling all new connections.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGUSR2</term>
+        <listitem>
+          <para>The <command>afpd</command> process will look in the message
+          directory configured at build time for a file named message.pid. For
+          each one found, a the contents will be sent as a message to the
+          associated AFP client. The file is removed after the message is
+          sent. This should only be sent to a child
+          <command>afpd</command>.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><filename>@pkgconfdir@/afp.conf</filename></term>
+
+        <listitem>
+          <para>configuration file used by afpd</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@localstatedir@/netatalk/afp_signature.conf</filename></term>
+
+        <listitem>
+          <para>list of server signature</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@localstatedir@/netatalk/afp_voluuid.conf</filename></term>
+
+        <listitem>
+          <para>list of UUID for Time Machine volume</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@pkgconfdir@/extmap.conf</filename></term>
+
+        <listitem>
+          <para>file name extension mapping</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>@pkgconfdir@/msg/message.pid</filename></term>
+
+        <listitem>
+          <para>contains messages to be sent to users.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>netatalk</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>hosts_access</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_signature.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp_voluuid.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>extmap.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/cnid_dbd.8.xml b/doc/manpages/man8/cnid_dbd.8.xml
new file mode 100644 (file)
index 0000000..9162047
--- /dev/null
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="cnid_dbd.8">
+  <refmeta>
+    <refentrytitle>cnid_dbd</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">01 Jan 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>cnid_dbd</refname>
+
+    <refpurpose>implement access to CNID databases through a dedicated daemon
+    process</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>cnid_dbd<indexterm>
+          <primary>cnid_dbd</primary>
+        </indexterm><indexterm>
+          <primary>CNID backend</primary>
+        </indexterm><indexterm>
+          <primary>NFS</primary>
+
+          <secondary>Network File System</secondary>
+        </indexterm></command>
+
+      <sbr />
+
+      <command>cnid_dbd<indexterm>
+          <primary>cnid_dbd</primary>
+        </indexterm></command>
+
+        <group choice="plain">
+          <arg choice="plain">-v</arg>
+          <arg choice="plain">-V</arg>
+        </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>cnid_dbd</command> provides an interface for storage and
+    retrieval of catalog node IDs (CNIDs) and related information to the
+    <emphasis remap="B">afpd</emphasis> daemon. CNIDs are a component of
+    Macintosh based file systems with semantics that map not easily onto Unix
+    file systems. This makes separate storage in a database necessary.
+    <command>cnid_dbd</command> is part of the <emphasis remap="B">CNID
+    backend</emphasis> framework of <emphasis remap="B">afpd</emphasis> and
+    implements the <emphasis remap="B">dbd</emphasis> backend.</para>
+
+    <para><command>cnid_dbd</command> is never started via the command line or
+    system startup scripts but only by the <emphasis
+    remap="B">cnid_metad</emphasis> daemon. There is one instance of
+    <command>cnid_dbd</command> per netatalk volume.</para>
+
+    <para><command>cnid_dbd</command> uses the <emphasis remap="B">Berkeley
+    DB</emphasis> database library and uses transactionally protected updates.
+    The <emphasis remap="B">dbd</emphasis> backend with transactions will
+    avoid corruption of the CNID database even if the system crashes
+    unexpectedly.</para>
+
+    <para><command>cnid_dbd</command> inherits the effective userid and
+    groupid from <emphasis remap="B">cnid_metad</emphasis> on startup, which
+    is normally caused by <emphasis remap="B">afpd</emphasis> serving a
+    netatalk volume to a client. It changes to the <emphasis
+    remap="B">Berkeley DB</emphasis> database home directory <emphasis
+    remap="I">dbdir</emphasis> that is associated with the volume. If the
+    userid inherited from <emphasis remap="B">cnid_metad</emphasis> is 0
+    (root), <command>cnid_dbd</command> will change userid and groupid to the
+    owner and group of the database home directory. Otherwise, it will
+    continue to use the inherited values. <command>cnid_dbd</command> will
+    then attempt to open the database and start serving requests using
+    filedescriptor <emphasis remap="I">clntfd</emphasis>. Subsequent instances
+    of <emphasis remap="B">afpd</emphasis> that want to access the same volume
+    are redirected to the running <command>cnid_dbd</command> process by
+    <emphasis remap="B">cnid_metad</emphasis> via the filedescriptor <emphasis
+    remap="I">ctrlfd</emphasis>.</para>
+
+    <para><command>cnid_dbd</command> can be configured to run forever or to
+    exit after a period of inactivity. If <command>cnid_dbd</command> receives
+    a TERM or an INT signal it will exit cleanly after flushing dirty database
+    buffers to disk and closing <emphasis remap="B">Berkeley DB</emphasis>
+    database environments. It is safe to terminate <command>cnid_dbd</command>
+    this way, it will be restarted when necessary. Other signals are not
+    handled and will cause an immediate exit, possibly leaving the CNID
+    database in an inconsistent state (no transactions) or losing recent
+    updates during recovery (transactions).</para>
+
+    <para>The <emphasis remap="B">Berkeley DB</emphasis> database subsystem
+    will create files named log.xxxxxxxxxx in the database home directory
+    <emphasis remap="I">dbdir</emphasis>, where xxxxxxxxxx is a monotonically
+    increasing integer. These files contain the transactional database
+    changes. They will be removed regularly, unless the <emphasis
+    remap="B">logfile_autoremove</emphasis> option is specified in the
+    <emphasis remap="I">db_param</emphasis> configuration file (see
+    below) with a value of 0 (default 1).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><option>-v, -V</option></term>
+       
+        <listitem>
+          <para>Show version and exit.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CONFIGURATION</title>
+
+    <para><command>cnid_dbd</command> reads configuration information from the
+    file <emphasis remap="I">db_param</emphasis> in the database directory
+    <emphasis remap="I">dbdir</emphasis> on startup. If the file does not
+    exist or a parameter is not listed, suitable default values are used. The
+    format for a single parameter is the parameter name, followed by one or
+    more spaces, followed by the parameter value, followed by a newline. The
+    following parameters are currently recognized:</para>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><emphasis remap="B">logfile_autoremove</emphasis></term>
+
+        <listitem>
+          <para>If set to 0, unused Berkeley DB transactional logfiles
+          (log.xxxxxxxxxx in the database home directory) are not removed on
+          startup of <command>cnid_dbd</command> and on a regular basis.
+          Default: 1.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">cachesize</emphasis></term>
+
+        <listitem>
+          <para>Determines the size of the Berkeley DB cache in kilobytes.
+          Default: 8192. Each <command>cnid_dbd</command> process grabs that
+          much memory on top of its normal memory footprint. It can be used to
+          tune database performance. The <emphasis
+          remap="B">db_stat</emphasis> utility with the <option>-m</option>
+          option that comes with Berkley DB can help you determine ether you
+          need to change this value. The default is pretty conservative so
+          that a large percentage of requests should be satisfied from the
+          cache directly. If memory is not a bottleneck on your system you
+          might want to leave it at that value. The <emphasis
+          remap="B">Berkeley DB Tutorial and Reference Guide</emphasis> has a
+          section <emphasis remap="B">Selecting a cache size</emphasis> that
+          gives more detailed information.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">flush_frequency</emphasis></term>
+
+        <term><emphasis remap="B">flush_interval</emphasis></term>
+
+        <listitem>
+          <para><emphasis remap="I">flush_frequency</emphasis> (Default: 1000)
+          and <emphasis remap="I">flush_interval</emphasis> (Default: 1800)
+          control how often changes to the database are checkpointed. Both of
+          these operations are performed if either i) more than <emphasis
+          remap="I">flush_frequency</emphasis> requests have been received or
+          ii) more than <emphasis remap="I">flush_interval</emphasis> seconds
+          have elapsed since the last save/checkpoint. Be careful to check
+          your harddisk configuration for on disk cache settings. Many IDE
+          disks just cache writes as the default behaviour, so even flushing
+          database files to disk will not have the desired effect.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">fd_table_size</emphasis></term>
+
+        <listitem>
+          <para>is the maximum number of connections (filedescriptors) that
+          can be open for <emphasis remap="B">afpd</emphasis> client processes
+          in <emphasis remap="B">cnid_dbd.</emphasis> Default: 512. If this
+          number is exceeded, one of the existing connections is closed and
+          reused. The affected <emphasis remap="B">afpd</emphasis> process
+          will transparently reconnect later, which causes slight overhead. On
+          the other hand, setting this parameter too high could affect
+          performance in <command>cnid_dbd</command> since all descriptors
+          have to be checked in a <function>select()</function> system call,
+          or worse, you might exceed the per process limit of open file
+          descriptors on your system. It is safe to set the value to 1 on
+          volumes where only one <emphasis remap="B">afpd</emphasis> client
+          process is expected to run, e.g. home directories.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><emphasis remap="B">idle_timeout</emphasis></term>
+
+        <listitem>
+          <para>is the number of seconds of inactivity before an idle
+          <command>cnid_dbd</command> exits. Default: 600. Set this to 0 to
+          disable the timeout.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>UPDATING</title>
+
+    <para>Note that the first version to appear <emphasis>after</emphasis>
+    Netatalk 2.1 ie Netatalk 2.1.1, will support BerkeleyDB updates on the fly
+    without manual intervention. In other words Netatalk 2.1 does contain code
+    to prepare the BerkeleyDB database for upgrades and to upgrade it in case
+    it has been prepared before. That means it can't upgrade a 2.0.x version
+    because that one didn't prepare the database.</para>
+
+    <para>In order to update between older Netatalk releases using different
+    BerkeleyDB library versions, follow this steps:</para>
+
+    <itemizedlist>
+      <listitem>
+        <para>Stop the to be upgraded old version of Netatalk</para>
+      </listitem>
+
+      <listitem>
+        <para>Using the old BerkeleyDB utilities run <command>db_recover -h
+        &lt;path to .AppleDB&gt;</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Using the new BerkeleyDB utilities run <command>db_upgrade -v -h
+        &lt;path to .AppleDB&gt; -f cnid2.db</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Again using the new BerkeleyDB utilities run
+        <command>db_checkpoint -1 -h &lt;path to .AppleDB&gt;</command></para>
+      </listitem>
+
+      <listitem>
+        <para>Start the the new version of Netatalk</para>
+      </listitem>
+    </itemizedlist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/cnid_metad.8.xml b/doc/manpages/man8/cnid_metad.8.xml
new file mode 100644 (file)
index 0000000..3bd833b
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="cnid_metad.8">
+  <refmeta>
+    <refentrytitle>cnid_metad</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">23 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>cnid_metad</refname>
+
+    <refpurpose>start cnid_dbd daemons on request</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>cnid_metad<indexterm>
+          <primary>cnid_metad</primary>
+        </indexterm></command>
+
+      <arg choice="opt">-d</arg>
+
+      <arg choice="opt"><arg choice="plain">-F </arg><arg
+      choice="plain"><replaceable>configuration file</replaceable></arg></arg>
+
+      <sbr />
+
+      <command>cnid_metad<indexterm>
+          <primary>cnid_metad</primary>
+        </indexterm></command>
+
+       <group choice="plain">
+         <arg choice="plain">-v</arg>
+         <arg choice="plain">-V</arg>
+       </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para><command>cnid_metad</command> waits for requests from <emphasis
+    remap="B">afpd</emphasis> to start up instances of the <emphasis
+    remap="B">cnid_dbd</emphasis> daemon. It keeps track of the status of a
+    <emphasis remap="B">cnid_dbd</emphasis> instance once started and will
+    restart it if necessary. <command>cnid_metad</command> is normally started
+    at boot time by <command>netatalk</command>(8) and runs
+    until shutdown.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist remap="TP">
+     <varlistentry>
+        <term><option>-d</option></term>
+
+        <listitem>
+          <para><emphasis remap="B">cnid_metad will remain in the foreground
+          and</emphasis> will also leave the standard input, standard output
+          and standard error file descriptors open. Useful for
+          debugging.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-F</option> <replaceable>configuration file</replaceable></term>
+
+        <listitem>
+          <para>Use <emphasis remap="I">configuration file</emphasis> as the
+            configuration file. The default is
+            <emphasis remap="I">@pkgconfdir@/afp.conf</emphasis>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v, -V</option></term>
+
+        <listitem>
+          <para>Show version and exit.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CAVEATS</title>
+
+    <para><command>cnid_metad</command> does not block or catch any signals
+    apart from SIGPIPE. It will therefore exit on most signals received. This
+    will also cause all instances of <emphasis remap="B">cnid_dbd's</emphasis>
+    started by that <command>cnid_metad</command> to exit gracefully. Since
+    state about and IPC access to the subprocesses is only maintained in
+    memory by <command>cnid_metad</command> this is desired behaviour. As soon
+    as <command>cnid_metad</command> is restarted <emphasis
+    remap="B">afpd</emphasis> processes will transparently reconnect.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>netatalk</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_dbd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>dbd</refentrytitle>
+
+        <manvolnum>1</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry></para>
+  </refsect1>
+</refentry>
diff --git a/doc/manpages/man8/netatalk.8.xml b/doc/manpages/man8/netatalk.8.xml
new file mode 100644 (file)
index 0000000..8c98011
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id="netatalk.8">
+  <refmeta>
+    <refentrytitle>netatalk</refentrytitle>
+
+    <manvolnum>8</manvolnum>
+
+    <refmiscinfo class="date">22 Mar 2012</refmiscinfo>
+
+    <refmiscinfo class="source">@NETATALK_VERSION@</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>netatalk</refname>
+
+    <refpurpose>Netatalk AFP server service controller daemon</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>netatalk<indexterm>
+          <primary>netatalk</primary>
+        </indexterm></command>
+
+      <sbr />
+
+      <command>netatalk<indexterm>
+          <primary>netatalk</primary>
+        </indexterm></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>netatalk</command> is the service controller daemon
+    responsible for starting and restarting the AFP daemon
+    <command>afpd</command> and the CNID daemon <command>cnid_metad</command>.
+    It is normally started at boot time from /etc/rc.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>SIGNALS</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>SIGTERM</term>
+
+        <listitem>
+          <para>Stop Netatalk service, AFP and CNID daemons</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>SIGHUP</term>
+
+        <listitem>
+          <para>Sending a <command>SIGHUP</command> will cause the AFP daemon
+          reload its configuration file.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><filename>@pkgconfdir@/afp.conf</filename></term>
+
+        <listitem>
+          <para>configuration file used by afpd and cnid_metad</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para><citerefentry>
+        <refentrytitle>afpd</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>cnid_metad</refentrytitle>
+
+        <manvolnum>8</manvolnum>
+      </citerefentry>, <citerefentry>
+        <refentrytitle>afp.conf</refentrytitle>
+
+        <manvolnum>5</manvolnum>
+      </citerefentry>.</para>
+  </refsect1>
+</refentry>
diff --git a/doc/manual/.gitignore b/doc/manual/.gitignore
new file mode 100644 (file)
index 0000000..8eb18aa
--- /dev/null
@@ -0,0 +1 @@
+manual.xml
diff --git a/doc/manual/Makefile.am b/doc/manual/Makefile.am
new file mode 100644 (file)
index 0000000..7f2b61d
--- /dev/null
@@ -0,0 +1,7 @@
+EXTRA_DIST = \
+       configuration.xml \
+       install.xml \
+       intro.xml \
+       upgrade.xml
+
+DISTCLEANFILES = manual.xml
diff --git a/doc/manual/configuration.xml b/doc/manual/configuration.xml
new file mode 100644 (file)
index 0000000..3bf6e0f
--- /dev/null
@@ -0,0 +1,1554 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="configuration">
+  <title>Setting up Netatalk</title>
+
+  <sect1>
+    <title>File Services<indexterm>
+        <primary>File Services</primary>
+
+        <secondary>Netatalk's File Services</secondary>
+      </indexterm></title>
+
+    <para>Netatalk supplies AFP<indexterm>
+        <primary>AFP</primary>
+
+        <secondary>Apple Filing Protocol</secondary>
+      </indexterm> services.</para>
+
+    <sect2>
+      <title>Setting up the AFP file server</title>
+
+      <para>AFP (the Apple Filing Protocol) is the protocol Apple Macintoshes
+      use for file services. The protocol has evolved over the years. The
+      latest changes to the protocol, called "AFP 3.3", were added with the
+      release of Snow Leopard<indexterm>
+          <primary>Snow Leopard</primary>
+
+          <secondary>Mac OS X 10.6</secondary>
+        </indexterm> (Mac OS X 10.6).</para>
+
+      <para>The afpd daemon offers the fileservices to Apple clients. The only
+      configuration file is <filename>afp.conf</filename>. It uses a ini style
+      configuration syntax.</para>
+
+      <para>Mac OS X 10.5 (Leopard) added support for Time Machine backups
+      over AFP. Two new functions ensure that backups are written to spinning
+      disk, not just in the server's cache. Different host operating systems
+      honour this cache flushing differently. To make a volume a Time Machine
+      target use the volume option "<option>time machine =
+      yes</option>".</para>
+
+      <para>Starting with Netatalk 2.1 UNIX symlinks<indexterm>
+          <primary>symlink</primary>
+
+          <secondary>UNIX symlink</secondary>
+        </indexterm> can be used on the server. Semantics are the same as for
+      eg NFS, ie they are not resolved on the server side but instead it's
+      completely up to the client to resolve them, resulting in links that
+      point somewhere inside the clients filesystem view.</para>
+
+      <sect3>
+        <title>afp.conf</title>
+
+        <para><filename>afp.conf</filename> is the configuration file used by
+        afpd to determine the behaviour and configuration of the AFP file
+        serverand the AFP volume that it provides.</para>
+
+        <para>The <filename>afp.conf</filename> is divided into several
+        sections:<variablelist>
+            <varlistentry>
+              <term>[Global]</term>
+
+              <listitem>
+                <para>The global section defines general server options</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term>[Homes]</term>
+
+              <listitem>
+                <para>The homes section defines user home volumes</para>
+              </listitem>
+            </varlistentry>
+          </variablelist>Any section not called <option>Global</option> or
+        <option>Homes</option> is interpreted as an AFP volume.</para>
+
+        <para>For sharing user homes by defining a <option>Homes</option>
+        section you must specify the option <option>basedir regex</option>
+        which can be a simple string with the path to the parent directory of
+        all user homes or a regular expression.</para>
+
+        <para>Example:</para>
+
+        <para><programlisting>[Homes]
+basedir regex = /home
+</programlisting></para>
+
+        <para>Now any user logging into the AFP server will have a user volume
+        available whos path is <filename>/home/NAME</filename>.</para>
+
+        <para>A more complex setup would be a server with a large amount of
+        user homes which are split across eg two different
+        filesystems:<itemizedlist>
+            <listitem>
+              <para>/RAID1/homes</para>
+            </listitem>
+
+            <listitem>
+              <para>/RAID2/morehomes</para>
+            </listitem>
+          </itemizedlist>The following configuration is
+        required:<programlisting>[Homes]
+basedir regex = /RAID./.*homes
+</programlisting></para>
+
+        <para>If <option>basedir regex</option> contains symlink, set the
+        canonicalized absolute path. When <filename>/home</filename> links to
+        <filename>/usr/home</filename>: <programlisting>[Homes]
+basedir regex = /usr/home</programlisting></para>
+
+        <para>For a more detailed explanation of the available options, please
+        refer to the <citerefentry>
+            <refentrytitle>afp.conf</refentrytitle>
+
+            <manvolnum>5</manvolnum>
+          </citerefentry> man page.</para>
+      </sect3>
+    </sect2>
+
+    <sect2 id="CNID-backends">
+      <title>CNID<indexterm>
+          <primary>CNID</primary>
+
+          <secondary>Catalog Node ID</secondary>
+        </indexterm> backends<indexterm>
+          <primary>Backend</primary>
+
+          <secondary>CNID backend</secondary>
+        </indexterm></title>
+
+      <para>Unlike other protocols like SMB or NFS, the AFP protocol mostly
+      refers to files and directories by ID and not by a path (the IDs are
+      also called CNID, that means Catalog Node ID). A typical AFP request
+      uses a directory ID<indexterm>
+          <primary>DID</primary>
+
+          <secondary>Directory ID</secondary>
+        </indexterm> and a filename, something like <phrase>"server, please
+      open the file named 'Test' in the directory with id 167"</phrase>. For
+      example "Aliases" on the Mac basically work by ID (with a fallback to
+      the absolute path in more recent AFP clients. But this applies only to
+      Finder, not to applications).</para>
+
+      <para>Every file in an AFP volume has to have a unique file ID<indexterm>
+          <primary>FID</primary>
+
+          <secondary>File ID</secondary>
+        </indexterm>, IDs must, according to the specs, never be reused, and
+      IDs are 32 bit numbers (Directory IDs use the same ID pool). So, after
+      ~4 billion files/folders have been written to an AFP volume, the ID pool
+      is depleted and no new file can be written to the volume. No whining
+      please :-)</para>
+
+      <para>Netatalk needs to map IDs to files and folders in the host
+      filesystem. To achieve this, several different CNID backends<indexterm>
+          <primary>CNID backend</primary>
+        </indexterm> are available and can be choosed by the <option>cnid
+      scheme</option><indexterm>
+          <primary>cnidscheme</primary>
+
+          <secondary>specifying a CNID backend</secondary>
+        </indexterm> option in the <citerefentry>
+          <refentrytitle>afp.conf</refentrytitle>
+
+          <manvolnum>5</manvolnum>
+        </citerefentry> configuration file. A CNID backend is basically a
+      database storing ID &lt;-&gt; name mappings.</para>
+
+      <para>The CNID Databases are by default located in
+      <filename>/var/netatalk/CNID</filename>.</para>
+
+      <para>There is a command line utility called <command>dbd</command>
+      available which can be used to verify, repair and rebuild the CNID
+      database.</para>
+
+      <note>
+        <para>There are some CNID related things you should keep in mind when
+        working with netatalk:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Don't nest volumes<indexterm>
+                <primary>Nested volumes</primary>
+              </indexterm>.</para>
+          </listitem>
+
+          <listitem>
+            <para>CNID backends are databases, so they turn afpd into a file
+            server/database mix.</para>
+          </listitem>
+
+          <listitem>
+            <para>If there's no more space on the filesystem left, the
+            database will get corrupted. You can work around this by either
+            using the <option>vol dbpath</option> option and put the database
+            files into another location or, if you use quotas, make sure the
+            CNID database folder is owned by a user/group without a
+            quota<indexterm>
+                <primary>Quotas</primary>
+
+                <secondary>Disk usage quotas</secondary>
+              </indexterm>.</para>
+          </listitem>
+
+          <listitem>
+            <para>Be careful with CNID databases for volumes that are mounted
+            via NFS. That is a pretty audacious decision to make anyway, but
+            putting a database there as well is really asking for trouble,
+            i.e. database corruption. Use the <option>vol dbpath</option>
+            directive to put the databases onto a local disk if you must use
+            NFS<indexterm>
+                <primary>NFS</primary>
+
+                <secondary>Network File System</secondary>
+              </indexterm> mounted volumes.</para>
+          </listitem>
+        </itemizedlist>
+      </note>
+
+      <sect3>
+        <title>cdb<indexterm>
+            <primary>CDB</primary>
+
+            <secondary>"cdb" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>The "concurrent database" backend is based on Berkeley DB. With
+        this backend, several afpd daemons access the CNID database directly.
+        Berkeley DB locking is used to synchronize access, if more than one
+        afpd process is active for a volume. The drawback is, that the crash
+        of a single afpd process might corrupt the database. cdb should only
+        be used when sharing home directories for a larger number of users
+        <emphasis>and</emphasis> it has been determined that a large number of
+        <command>cnid_dbd</command> processes is problematic.</para>
+      </sect3>
+
+      <sect3>
+        <title>dbd<indexterm>
+            <primary>DBD</primary>
+
+            <secondary>"dbd" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>Access to the CNID database is restricted to the cnid_dbd daemon
+        process. afpd processes communicate with the daemon for database reads
+        and updates. The probability for database corruption is practically
+        zero.</para>
+
+        <para>This is the default backend since Netatalk 2.1.</para>
+      </sect3>
+
+      <sect3>
+        <title>tdb<indexterm>
+            <primary>tdb</primary>
+
+            <secondary>"tdb" CNID backend</secondary>
+          </indexterm></title>
+
+        <para><abbrev>tdb</abbrev> is another persistent CNID database, it's
+        Samba's <emphasis>Trivial Database</emphasis>. It could be used
+        instead of <abbrev>cdb</abbrev> for user volumes.<important>
+            <para>Only ever use it for volumes that are
+            <emphasis>not</emphasis> shared and accessed by multiple clients
+            at once !</para>
+          </important>This backend is also used internally (as in-memory CNID
+        database) as a fallback in case opening the primary database can't be
+        opened, because <abbrev>tdb</abbrev> can work as in-memory database.
+        This of course means upon restart the CNIDs are gone.</para>
+      </sect3>
+
+      <sect3>
+        <title>last<indexterm>
+            <primary>Last</primary>
+
+            <secondary>"last" CNID backend</secondary>
+          </indexterm></title>
+
+        <para>The last backend is a in-memory tdb database. It is not
+        persistent. Starting with netatalk 3.0, it becomes the <emphasis> read
+        only mode</emphasis> automatically. This is useful e.g. for
+        CD-ROMs.</para>
+      </sect3>
+    </sect2>
+
+    <sect2 id="charsets">
+      <title>Charsets<indexterm>
+          <primary>Charset</primary>
+
+          <secondary>character set</secondary>
+        </indexterm>/Unicode<indexterm>
+          <primary>Unicode</primary>
+        </indexterm></title>
+
+      <para></para>
+
+      <sect3>
+        <title>Why Unicode?</title>
+
+        <para>Internally, computers don't know anything about characters and
+        texts, they only know numbers. Therefore, each letter is assigned a
+        number. A character set, often referred to as
+        <emphasis>charset</emphasis> or
+        <emphasis>codepage</emphasis><indexterm>
+            <primary>Codepage</primary>
+          </indexterm>, defines the mappings between numbers and
+        letters.</para>
+
+        <para>If two or more computer systems need to communicate with each
+        other, the have to use the same character set. In the 1960s the
+        ASCII<indexterm>
+            <primary>ASCII</primary>
+
+            <secondary>American Standard Code for Information
+            Interchange</secondary>
+          </indexterm> (American Standard Code for Information Interchange)
+        character set was defined by the American Standards Association. The
+        original form of ASCII represented 128 characters, more than enough to
+        cover the English alphabet and numerals. Up to date, ASCII has been
+        the normative character scheme used by computers.</para>
+
+        <para>Later versions defined 256 characters to produce a more
+        international fluency and to include some slightly esoteric graphical
+        characters. Using this mode of encoding each character takes exactly
+        one byte. Obviously, 256 characters still wasn't enough to map all the
+        characters used in the various languages into one character
+        set.</para>
+
+        <para>As a result localized character sets were defined later, e.g the
+        ISO-8859 character sets. Most operating system vendors introduced
+        their own characters sets to satisfy their needs, e.g. IBM defined the
+        <emphasis>codepage 437 (DOSLatinUS)</emphasis>, Apple introduced the
+        <emphasis>MacRoman</emphasis><indexterm>
+            <primary>MacRoman</primary>
+
+            <secondary>MacRoman charset</secondary>
+          </indexterm> codepage and so on. The characters that were assigned
+        number larger than 127 were referred to as
+        <emphasis>extended</emphasis> characters. These character sets
+        conflict with another, as they use the same number for different
+        characters, or vice versa.</para>
+
+        <para>Almost all of those characters sets defined 256 characters,
+        where the first 128 (0-127) character mappings are identical to ASCII.
+        As a result, communication between systems using different codepages
+        was effectively limited to the ASCII charset.</para>
+
+        <para>To solve this problem new, larger character sets were defined.
+        To make room for more character mappings, these character sets use at
+        least 2 bytes to store a character. They are therefore referred to as
+        <emphasis>multibyte</emphasis> character sets.</para>
+
+        <para>One standardized multibyte charset encoding scheme is known as
+        <ulink url="http://www.unicode.org/">unicode</ulink>. A big advantage
+        of using a multibyte charset is that you only need one. There is no
+        need to make sure two computers use the same charset when they are
+        communicating.</para>
+      </sect3>
+
+      <sect3>
+        <title>character sets used by Apple</title>
+
+        <para>In the past, Apple clients used single-byte charsets to
+        communicate over the network. Over the years Apple defined a number of
+        codepages, western users will most likely be using the
+        <emphasis>MacRoman</emphasis> codepage.</para>
+
+        <para>Codepages defined by Apple include:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>MacArabic, MacFarsi</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCentralEurope</para>
+          </listitem>
+
+          <listitem>
+            <para>MacChineseSimple</para>
+          </listitem>
+
+          <listitem>
+            <para>MacChineseTraditional</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCroation</para>
+          </listitem>
+
+          <listitem>
+            <para>MacCyrillic</para>
+          </listitem>
+
+          <listitem>
+            <para>MacDevanagari</para>
+          </listitem>
+
+          <listitem>
+            <para>MacGreek</para>
+          </listitem>
+
+          <listitem>
+            <para>MacHebrew</para>
+          </listitem>
+
+          <listitem>
+            <para>MacIcelandic</para>
+          </listitem>
+
+          <listitem>
+            <para>MacJapanese</para>
+          </listitem>
+
+          <listitem>
+            <para>MacKorean</para>
+          </listitem>
+
+          <listitem>
+            <para>MacRoman</para>
+          </listitem>
+
+          <listitem>
+            <para>MacRomanian</para>
+          </listitem>
+
+          <listitem>
+            <para>MacThai</para>
+          </listitem>
+
+          <listitem>
+            <para>MacTurkish</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>Starting with Mac OS X and AFP3, <ulink
+        url="http://www.utf-8.com/">UTF-8</ulink> is used. UTF-8 encodes
+        Unicode characters in an ASCII compatible way, each Unicode character
+        is encoded into 1-6 ASCII characters. UTF-8 is therefore not really a
+        charset itself, it's an encoding of the Unicode charset.</para>
+
+        <para>To complicate things, Unicode defines several <emphasis> <ulink
+        url="http://www.unicode.org/reports/tr15/index.html">normalization</ulink>
+        </emphasis> forms. While <ulink
+        url="http://www.samba.org">samba</ulink><indexterm>
+            <primary>Samba</primary>
+          </indexterm> uses <emphasis>precomposed</emphasis><indexterm>
+            <primary>Precomposed</primary>
+
+            <secondary>Precomposed Unicode normalization</secondary>
+          </indexterm> Unicode, which most Unix tools prefer as well, Apple
+        decided to use the <emphasis>decomposed</emphasis><indexterm>
+            <primary>Decomposed</primary>
+
+            <secondary>Decomposed Unicode normalization</secondary>
+          </indexterm> normalization.</para>
+
+        <para>For example lets take the German character
+        '<keycode>ä</keycode>'. Using the precomposed normalization, Unicode
+        maps this character to 0xE4. In decomposed normalization, 'ä' is
+        actually mapped to two characters, 0x61 and 0x308. 0x61 is the mapping
+        for an 'a', 0x308 is the mapping for a <emphasis>COMBINING
+        DIAERESIS</emphasis>.</para>
+
+        <para>Netatalk refers to precomposed UTF-8 as
+        <emphasis>UTF8</emphasis><indexterm>
+            <primary>UTF8</primary>
+
+            <secondary>Netatalk's precomposed UTF-8 encoding</secondary>
+          </indexterm> and to decomposed UTF-8 as
+        <emphasis>UTF8-MAC</emphasis><indexterm>
+            <primary>UTF8-MAC</primary>
+
+            <secondary>Netatalk's decomposed UTF-8 encoding</secondary>
+          </indexterm>.</para>
+      </sect3>
+
+      <sect3>
+        <title>afpd and character sets</title>
+
+        <para>To support new AFP 3.x and older AFP 2.x clients at the same
+        time, afpd needs to be able to convert between the various charsets
+        used. AFP 3.x clients always use UTF8-MAC, AFP 2.x clients use one of
+        the Apple codepages.</para>
+
+        <para>At the time of this writing, netatalk supports the following
+        Apple codepages:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>MAC_CENTRALEUROPE</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CHINESE_SIMP</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CHINESE_TRAD</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_CYRILLIC</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_GREEK</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_HEBREW</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_JAPANESE</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_KOREAN</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_ROMAN</para>
+          </listitem>
+
+          <listitem>
+            <para>MAC_TURKISH</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>afpd handles three different character set options:</para>
+
+        <variablelist>
+          <varlistentry>
+            <term>unix charset<indexterm>
+                <primary>unix charset</primary>
+
+                <secondary>afpd's unix charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>This is the codepage used internally by your operating
+              system. If not specified, it defaults to <option>UTF8</option>.
+              If <option>LOCALE</option> is specified and your system support
+              Unix locales, afpd tries to detect the codepage. afpd uses this
+              codepage to read its configuration files, so you can use
+              extended characters for volume names, login messages, etc. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>mac charset<indexterm>
+                <primary>mac charset</primary>
+
+                <secondary>afpd's mac charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>As already mentioned, older Mac OS clients (up to AFP 2.2)
+              use codepages to communicate with afpd. However, there is no
+              support for negotiating the codepage used by the client in the
+              AFP protocol. If not specified otherwise, afpd assumes the
+              <emphasis>MacRoman</emphasis> codepage is used. In case you're
+              clients use another codepage, e.g.
+              <emphasis>MacCyrillic</emphasis>, you'll <emphasis
+              role="bold">have</emphasis> to explicitly configure this. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>vol charset<indexterm>
+                <primary>vol charset</primary>
+
+                <secondary>afpd's vol charset setting</secondary>
+              </indexterm></term>
+
+            <listitem>
+              <para>This defines the charset afpd should use for filenames on
+              disk. By default, it is the same as <option>unix
+              charset</option>. If you have <ulink
+              url="http://www.gnu.org/software/libiconv/">iconv</ulink><indexterm>
+                  <primary>Iconv</primary>
+
+                  <secondary>iconv encoding conversion engine</secondary>
+                </indexterm> installed, you can use any iconv provided charset
+              as well.</para>
+
+              <para>afpd needs a way to preserve extended macintosh
+              characters, or characters illegal in unix filenames, when saving
+              files on a unix filesystem. Earlier versions used the the so
+              called CAP encoding<indexterm>
+                  <primary>CAP encoding</primary>
+
+                  <secondary>CAP style character encoding</secondary>
+                </indexterm>. An extended character (&gt;0x7F) would be
+              converted to a :xx hex sequence, e.g. the Apple Logo (MacRoman:
+              0xF0) was saved as :f0. Some special characters will be
+              converted as to :xx notation as well. '/' will be encoded to
+              :2f, if <option>usedots</option> was not specified, a leading
+              dot '.' will be encoded as :2e.</para>
+
+              <para>Even though this version now uses <option>UTF8</option> as
+              the default encoding for filenames, '/' will be converted to
+              ':'. For western users another useful setting could be
+              <option>vol charset = ISO-8859-15</option>.</para>
+
+              <para>If a character cannot be converted from the <option>mac
+              charset</option> to the selected <option>vol charset</option>,
+              afpd will save it as a CAP encoded character. For AFP3 clients,
+              afpd will convert the UTF8 character to <option>mac
+              charset</option> first. If this conversion fails, you'll receive
+              a -50 error on the mac. <emphasis>Note</emphasis>: Whenever you
+              can, please stick with the default UTF8 volume format. see
+              <citerefentry>
+                  <refentrytitle>afp.conf</refentrytitle>
+
+                  <manvolnum>5</manvolnum>
+                </citerefentry>.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </sect3>
+    </sect2>
+
+    <sect2 id="authentication">
+      <title>Authentication<indexterm>
+          <primary>Authentication</primary>
+
+          <secondary>between AFP client and server</secondary>
+        </indexterm></title>
+
+      <sect3>
+        <title>AFP authentication basics</title>
+
+        <para>Apple chose a flexible model called "User Authentication
+        Modules"<indexterm>
+            <primary>UAM</primary>
+
+            <secondary>User Authentication Module</secondary>
+          </indexterm> (UAMs) for authentication purposes between AFP client
+        and server. An AFP client initially connecting to an AFP server will
+        ask for the list of UAMs which the server provides, and will choose
+        the one with strongest encryption that the client supports.</para>
+
+        <para>Several UAMs have been developed by Apple over the time, some by
+        3rd-party developers.</para>
+      </sect3>
+
+      <sect3>
+        <title>UAMs supported by Netatalk</title>
+
+        <para>Netatalk supports the following ones by default:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>"No User Authent"<indexterm>
+                <primary>No User Authent</primary>
+
+                <secondary>"No User Authent" UAM (guest access)</secondary>
+              </indexterm> UAM (guest access without authentication)</para>
+          </listitem>
+
+          <listitem>
+            <para>"Cleartxt Passwrd"<indexterm>
+                <primary>Cleartxt Passwrd</primary>
+
+                <secondary>"Cleartxt Passwrd" UAM</secondary>
+              </indexterm> UAM (no password encryption)</para>
+          </listitem>
+
+          <listitem>
+            <para>"Randnum exchange"<indexterm>
+                <primary>Randnum exchange</primary>
+
+                <secondary>"Randnum exchange" UAM</secondary>
+              </indexterm>/"2-Way Randnum exchange"<indexterm>
+                <primary>2-Way Randnum exchange</primary>
+
+                <secondary>"2-Way Randnum exchange" UAM</secondary>
+              </indexterm> UAMs (weak password encryption, separate password
+            storage)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHCAST128"<indexterm>
+                <primary>DHCAST128</primary>
+
+                <secondary>"DHCAST128" UAM</secondary>
+              </indexterm> UAM (stronger password encryption)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHX2"<indexterm>
+                <primary>DHX2</primary>
+
+                <secondary>"DHX2" UAM</secondary>
+              </indexterm> UAM (successor of DHCAST128)</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>There exist other optional UAMs as well:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>"PGPuam 1.0"<indexterm>
+                <primary>PGPuam 1.0</primary>
+
+                <secondary>"PGPuam 1.0" UAM</secondary>
+              </indexterm><indexterm>
+                <primary>uams_pgp.so</primary>
+
+                <secondary>"PGPuam 1.0" UAM</secondary>
+              </indexterm> UAM (PGP-based authentication for pre-Mac OS X
+            clients. You'll also need the <ulink
+            url="http://www.vmeng.com/vinnie/papers/pgpuam.html">PGPuam
+            client</ulink> to let this work)</para>
+
+            <para>You'll have to add <filename>"--enable-pgp-uam"</filename>
+            to your configure switches to have this UAM available.</para>
+          </listitem>
+
+          <listitem>
+            <para>"Kerberos IV"<indexterm>
+                <primary>Kerberos IV</primary>
+
+                <secondary>"Kerberos IV" UAM</secondary>
+              </indexterm><indexterm>
+                <primary>uams_krb4.so</primary>
+
+                <secondary>"Kerberos IV" UAM</secondary>
+              </indexterm>/"AFS Kerberos"<indexterm>
+                <primary>AFS Kerberos</primary>
+
+                <secondary>"AFS Kerberos" UAM (Kerberos IV)</secondary>
+              </indexterm> UAMs (suitable to use <ulink
+            url="http://web.mit.edu/macdev/KfM/Common/Documentation/faq.html">Kerberos
+            v4 based authentication</ulink> and AFS file servers)</para>
+
+            <para>Use <filename>"--enable-krb4-uam"</filename> at compile time
+            to activate the build of this UAM.</para>
+          </listitem>
+
+          <listitem>
+            <para>"Client Krb v2"<indexterm>
+                <primary>Client Krb v2</primary>
+
+                <secondary>"Client Krb v2" UAM (Kerberos V)</secondary>
+              </indexterm> UAM (Kerberos V, suitable for "Single Sign On"
+            Scenarios with OS X clients -- see below)</para>
+
+            <para><filename>"--enable-krbV-uam"</filename> will provide you
+            with the ability to use this UAM.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>You can configure which UAMs should be activated by defining
+        "<option>uam list</option>" in <option>Global</option> section.
+        <command>afpd</command> will log which UAMs it's using and if problems
+        occur while activating them in either
+        <filename>netatalk.log</filename> or syslog at startup time.
+        <citerefentry>
+            <refentrytitle>asip-status.pl</refentrytitle>
+
+            <manvolnum>1</manvolnum>
+          </citerefentry> can be used to query the available UAMs of AFP
+        servers as well.</para>
+
+        <para>Having a specific UAM available at the server does not
+        automatically mean that a client can use it. Client-side support is
+        also necessary. For older Macintoshes running Mac OS &lt; X DHCAST128
+        support exists since AppleShare client 3.8.x.</para>
+
+        <para>On OS X, there exist some client-side techniques to make the
+        AFP-client more verbose, so one can have a look what's happening while
+        negotiating the UAMs to use. Compare with this <ulink
+        url="http://article.gmane.org/gmane.network.netatalk.devel/7383/">hint</ulink>.</para>
+      </sect3>
+
+      <sect3>
+        <title>Which UAMs to activate?</title>
+
+        <para>It depends primarily on your needs and on the kind of Mac OS
+        versions you have to support. Basically one should try to use
+        DHCAST128 and DHX2 where possible because of its strength of password
+        encryption.</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Unless you really have to supply guest access to your
+            server's volumes ensure that you disable "No User Authent" since
+            it might lead accidentally to unauthorized access. In case you
+            must enable guest access take care that you enforce this on a per
+            volume base using the access controls.</para>
+          </listitem>
+
+          <listitem>
+            <para>The "ClearTxt Passwrd" UAM is as bad as it sounds since
+            passwords go unencrypted over the wire. Try to avoid it at both
+            the server's side as well as on the client's. Note: If you want to
+            provide Mac OS 8/9 clients with NetBoot-services then you need
+            uams_cleartext.so since the AFP-client integrated into the Mac's
+            firmware can only deal with this basic form of
+            authentication.</para>
+          </listitem>
+
+          <listitem>
+            <para>Since "Randnum exchange"/"2-Way Randnum exchange" uses only
+            56 bit DES for encryption it should be avoided as well. Another
+            disadvantage is the fact that the passwords have to be stored in
+            cleartext on the server and that it doesn't integrate into both
+            PAM scenarios or classic /etc/shadow (you have to administrate
+            passwords separately by using the <citerefentry>
+                <refentrytitle>afppasswd</refentrytitle>
+
+                <manvolnum>1</manvolnum>
+              </citerefentry> utility, if clients should use these
+            UAMs)</para>
+          </listitem>
+
+          <listitem>
+            <para>"DHCAST128" or "DHX2" should be a good compromise for most
+            people since it combines stronger encryption with PAM
+            integration.</para>
+          </listitem>
+
+          <listitem>
+            <para>Using the Kerberos V<indexterm>
+                <primary>Kerberos V</primary>
+
+                <secondary>"Client Krb v2" UAM</secondary>
+              </indexterm> ("Client Krb v2") UAM, it's possible to implement
+            real single sign on scenarios using Kerberos tickets. The password
+            is not sent over the network. Instead, the user password is used
+            to decrypt a service ticket for the appleshare server. The service
+            ticket contains an encryption key for the client and some
+            encrypted data (which only the appleshare server can decrypt). The
+            encrypted portion of the service ticket is sent to the server and
+            used to authenticate the user. Because of the way that the afpd
+            service principal detection is implemented, this authentication
+            method is vulnerable to man-in-the-middle attacks.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>For a more detailed overview over the technical implications of
+        the different UAMs, please have a look at Apple's <ulink
+        url="http://developer.apple.com/library/mac/#documentation/Networking/Conceptual/AFP/AFPSecurity/AFPSecurity.html#//apple_ref/doc/uid/TP40000854-CH232-SW1">File
+        Server Security</ulink> pages.</para>
+      </sect3>
+
+      <sect3>
+        <title>Using different authentication sources with specific
+        UAMs</title>
+
+        <para>Some UAMs provide the ability to use different authentication
+        "backends", namely <filename>uams_cleartext.so</filename>,
+        <filename>uams_dhx.so</filename> and
+        <filename>uams_dhx2.so</filename>. They can use either classic Unix
+        passwords from <filename>/etc/passwd</filename>
+        (<filename>/etc/shadow</filename>) or PAM if the system supports that.
+        <filename>uams_cleartext.so</filename> can be symlinked to either
+        <filename>uams_passwd.so</filename> or
+        <filename>uams_pam.so</filename>, <filename>uams_dhx.so</filename> to
+        <filename>uams_dhx_passwd.so</filename> or
+        <filename>uams_dhx_pam.so</filename> and
+        <filename>uams_dhx2.so</filename> to
+        <filename>uams_dhx2_passwd.so</filename> or
+        <filename>uams_dhx2_pam.so</filename>.</para>
+
+        <para>So, if it looks like this in Netatalk's UAMs folder (per default
+        <filename>/etc/netatalk/uams/</filename>):<programlisting>uams_clrtxt.so -&gt; uams_pam.so
+uams_dhx.so -&gt; uams_dhx_pam.so
+uams_dhx2.so -&gt; uams_dhx2_pam.so</programlisting> then you're using PAM,
+        otherwise classic Unix passwords. The main advantage of using PAM is
+        that one can integrate Netatalk in centralized authentication
+        scenarios, eg. via LDAP, NIS and the like. Please always keep in mind
+        that the protection of your user's login credentials in such scenarios
+        also depends on the strength of encryption that the UAM in question
+        supplies. So think about eliminating weak UAMs like "ClearTxt Passwrd"
+        and "Randnum exchange" completely from your network.</para>
+      </sect3>
+
+      <sect3>
+        <title>Netatalk UAM overview table</title>
+
+        <para>A small overview of the most common used UAMs.</para>
+
+        <table orient="land">
+          <title>Netatalk UAM overview</title>
+
+          <tgroup align="center" cols="7">
+            <colspec colname="col1" colnum="1" colwidth="0.5*" />
+
+            <colspec colname="uam_guest" colnum="2" colwidth="1*" />
+
+            <colspec colname="uam_clrtxt" colnum="3" colwidth="1*" />
+
+            <colspec colname="uam_randnum" colnum="4" colwidth="1*" />
+
+            <colspec colname="uam_dhx" colnum="5" colwidth="1*" />
+
+            <colspec colname="uam_dhx2" colnum="6" colwidth="1*" />
+
+            <colspec colname="uam_gss" colnum="7" colwidth="1*" />
+
+            <tbody>
+              <row>
+                <entry align="center" rotate="0" valign="middle">UAM</entry>
+
+                <entry>No User Authent<indexterm>
+                    <primary>uams_guest.so</primary>
+
+                    <secondary>"No User Authent" UAM (guest
+                    access)</secondary>
+                  </indexterm></entry>
+
+                <entry>Cleartxt Passwrd<indexterm>
+                    <primary>uams_cleartxt.so</primary>
+
+                    <secondary>"Cleartxt Passwrd" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>(2-Way) Randnum exchange<indexterm>
+                    <primary>uams_randnum.so</primary>
+
+                    <secondary>"(2-Way) Randnum exchange" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>DHCAST128<indexterm>
+                    <primary>uams_dhx.so</primary>
+
+                    <secondary>"DHCAST128" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>DHX2<indexterm>
+                    <primary>uams_dhx2.so</primary>
+
+                    <secondary>"DHX2" UAM</secondary>
+                  </indexterm></entry>
+
+                <entry>Client Krb v2<indexterm>
+                    <primary>uams_gss.so</primary>
+
+                    <secondary>"Client Krb v2" UAM (Kerberos V)</secondary>
+                  </indexterm></entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">pssword
+                length</entry>
+
+                <entry>guest access</entry>
+
+                <entry>max. 8 characters</entry>
+
+                <entry>max. 8 characters</entry>
+
+                <entry>max. 64 characters</entry>
+
+                <entry>max. 256 characters</entry>
+
+                <entry>Kerberos tickets</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Client
+                support</entry>
+
+                <entry>built-in into all Mac OS versions</entry>
+
+                <entry>built-in in all Mac OS versions except 10.0. Has to be
+                activated explicitly in recent Mac OS X versions</entry>
+
+                <entry>built-in into almost all Mac OS versions</entry>
+
+                <entry>built-in since AppleShare client 3.8.4, available as a
+                plug-in for 3.8.3, integrated in Mac OS X' AFP client</entry>
+
+                <entry>built-in since Mac OS X 10.2</entry>
+
+                <entry>built-in since Mac OS X 10.2</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0"
+                valign="middle">Encryption</entry>
+
+                <entry>Enables guest access without authentication between
+                client and server.</entry>
+
+                <entry>Password will be sent in cleartext over the wire. Just
+                as bad as it sounds, therefore avoid at all if possible (note:
+                providing NetBoot services requires the ClearTxt UAM)</entry>
+
+                <entry>8-byte random numbers are sent over the wire,
+                comparable with DES, 56 bits. Vulnerable to offline dictionary
+                attack. Requires passwords in clear on the server.</entry>
+
+                <entry>Password will be encrypted with 128 bit SSL, user will
+                be authenticated against the server but not vice versa.
+                Therefor weak against man-in-the-middle attacks.</entry>
+
+                <entry>Password will be encrypted using libgcrypt with CAST
+                128 in CBC mode. User will be authenticated against the server
+                but not vice versa. Therefor weak against man-in-the-middle
+                attacks.</entry>
+
+                <entry>Password is not sent over the network. Due to the
+                service principal detection method, this authentication method
+                is vulnerable to man-in-the-middle attacks.</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Server
+                support</entry>
+
+                <entry align="center" valign="middle">uams_guest.so</entry>
+
+                <entry align="center" valign="middle">uams_cleartxt.so</entry>
+
+                <entry align="center" valign="middle">uams_randnum.so</entry>
+
+                <entry align="center" valign="middle">uams_dhx.so</entry>
+
+                <entry align="center" valign="middle">uams_dhx2.so</entry>
+
+                <entry align="center" valign="middle">uams_gss.so</entry>
+              </row>
+
+              <row>
+                <entry align="center" rotate="0" valign="middle">Password
+                storage method</entry>
+
+                <entry align="center" valign="middle">None</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">Passwords stored in
+                clear text in a separate text file</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">Either /etc/passwd
+                (/etc/shadow) or PAM</entry>
+
+                <entry align="center" valign="middle">At the Kerberos Key
+                Distribution Center*</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
+        <para>* Have a look at this <ulink
+        url="http://cryptnet.net/fdp/admin/kerby-infra/en/kerby-infra.html">Kerberos
+        overview</ulink></para>
+      </sect3>
+
+      <sect3 id="sshtunnel">
+        <title>SSH tunneling</title>
+
+        <para>Tunneling and all sort of VPN stuff has nothing to do with AFP
+        authentication and UAMs in general. But since Apple introduced an
+        option called "Allow Secure Connections Using SSH" and many people
+        tend to confuse both things, we'll speak about that here too.</para>
+
+        <sect4 id="manualsshtunnel">
+          <title>Manually tunneling an AFP session</title>
+
+          <para>This works since the first AFP servers that spoke "AFP over
+          TCP" appeared in networks. One simply tunnels the remote server's
+          AFP port to a local port different than 548 and connects locally to
+          this port afterwards. On OS X this can be done by</para>
+
+          <programlisting>ssh -l $USER $SERVER -L 10548:127.0.0.1:548 sleep 3000</programlisting>
+
+          <para>After establishing the tunnel one will use
+          <filename>"afp://127.0.0.1:10548"</filename> in the "Connect to
+          server" dialog. All AFP traffic including the initial connection
+          attempts will be sent encrypted over the wire since the local AFP
+          client will connect to the Mac's local port 10548 which will be
+          forwarded to the remote server's AFP port (we used the default 548)
+          over SSH.</para>
+
+          <para>These sorts of tunnels are an ideal solution if you've to
+          access an AFP server providing weak authentications mechanisms
+          through the Internet without having the ability to use a "real" VPN.
+          Note that you can let <command>ssh</command> compress the data by
+          using its "-C" switch and that the tunnel endpoints can be different
+          from both AFP client and server (compare with the SSH documentation
+          for details).</para>
+        </sect4>
+
+        <sect4 id="autosshtunnel">
+          <title>Automatically establishing a tunneled AFP connection</title>
+
+          <para>From Mac OS X 10.2 to 10.4, Apple added an "Allow Secure
+          Connections Using SSH" checkbox to the "Connect to Server" dialog.
+          The idea behind: When the server signals that it can be contacted by
+          SSH then Mac OS X' AFP client tries to establish the tunnel and
+          automagically sends all AFP traffic through it.</para>
+
+          <para>But it took until the release of Mac OS X 10.3 that this
+          feature worked the first time... partly. In case, the SSH tunnel
+          can't be established the AFP client <emphasis
+          role="strong">silently</emphasis> fell back to an unencrypted AFP
+          connection attempt.</para>
+
+          <para>Netatalk's afpd will report that it is capable of handling SSH
+          tunneled AFP requests, when both "<option>advertise ssh</option>"
+          and "<option>fqdn</option>" options are set in
+          <option>Global</option> section (double check with <citerefentry>
+              <refentrytitle>asip-status.pl</refentrytitle>
+
+              <manvolnum>1</manvolnum>
+            </citerefentry> after you restarted afpd when you made changes to
+          the settings). But there are a couple of reasons why you don't want
+          to use this option at all:</para>
+
+          <itemizedlist>
+            <listitem>
+              <para>Tunneling TCP over TCP (as SSH does) is not the best idea.
+              There exist better solutions like VPNs based on the IP
+              layer.</para>
+            </listitem>
+
+            <listitem>
+              <para>Since this SSH kludge isn't a normal UAM that integrates
+              directly into the AFP authentication mechanisms but instead uses
+              a single flag signalling clients whether they can <emphasis
+              role="strong">try</emphasis> to establish a tunnel or not, it
+              makes life harder to see what's happening when things go
+              wrong.</para>
+            </listitem>
+
+            <listitem>
+              <para>You cannot control which machines are logged on by
+              Netatalk tools like a <command>macusers</command> since all
+              connection attempts seem to be made from localhost.</para>
+            </listitem>
+
+            <listitem>
+              <para>On the other side you've to limit access to afpd to
+              localhost only (TCP wrappers) when you want to ensure that all
+              AFP sessions are SSH encrypted or...</para>
+            </listitem>
+
+            <listitem>
+              <para>...when you're using 10.2 - 10.3.3 then you get the
+              opposite of what you'd expect: potentially unencrypted AFP
+              communication (including logon credentials) on the network
+              without a single notification that establishing the tunnel
+              failed. Apple fixed that not until Mac OS X 10.3.4.</para>
+            </listitem>
+
+            <listitem>
+              <para>Encrypting all AFP sessions via SSH can lead to a
+              significantly higher load on the Netatalk server</para>
+            </listitem>
+          </itemizedlist>
+        </sect4>
+      </sect3>
+    </sect2>
+
+    <sect2 id="acls">
+      <title>ACL Support<indexterm>
+          <primary>ACLs</primary>
+        </indexterm></title>
+
+      <para>ACL support for AFP is implemented for ZFS ACLs on Solaris and
+      derived platforms and for POSIX 1e ACLs on Linux.</para>
+
+      <sect3>
+        <title>Configuration</title>
+
+        <para>For a basic mode of operation there's nothing to configure.
+        Netatalk reads ACLs on the fly and calculates effective permissions
+        which are then send to the AFP client via the so called
+        UARights<indexterm>
+            <primary>UARights</primary>
+          </indexterm> permission bits. On a Mac, the Finder uses these bits
+        to adjust permission in Finder windows. For example folder whos UNIX
+        mode would only result in in read-only permissions for a user will not
+        be displayed with a read-only icon and the user will be able to write
+        to the folder given the folder has an ACL giving the user write
+        access.</para>
+
+        <para>By default, the effective permission of the authenticated user
+        are only mapped to the mentioned UARights<indexterm>
+            <primary>UARights</primary>
+          </indexterm>permission structure, not the UNIX mode. You can adjust
+        this behaviour with the configuration option <link
+        linkend="map_acls">map acls</link>.</para>
+
+        <para>However, neither in Finder "Get Info" windows nor in Terminal
+        will you be able to see the ACLs, that's a result of how ACLs in OS X
+        are designed. If you want to be able to display ACLs on the client,
+        things get more involved as you must then setup both client and server
+        to be part on a authentication domain (directory service, eg LDAP,
+        OpenDirectory). The reason is, that in OS X ACLs are bound to UUIDs,
+        not just uid's or gid's. Therefor afpd must be able to map every
+        filesystem uid and gid to a UUID so that it can return the server side
+        ACLs which are bound to UNIX uid and gid mapped to OS X UUIDs.</para>
+
+        <para>Netatalk can query a directory server using LDAP queries. Either
+        the directory server already provides an UUID attribute for user and
+        groups (Active Directory, Open Directory) or you reuse an unused
+        attribute (or add a new one) to you directory server (eg
+        OpenLDAP).</para>
+
+        <para>In detail:</para>
+
+        <orderedlist>
+          <listitem>
+            <para>For Solaris/ZFS: ZFS Volumes</para>
+
+            <para>You should configure a ZFS ACL know for any volume you want
+            to use with Netatalk:</para>
+
+            <screen>aclinherit = passthrough
+aclmode = passthrough</screen>
+
+            <para>For an explanation of what this knob does and how to apply
+            it, check your hosts ZFS documentation (eg man zfs).</para>
+          </listitem>
+
+          <listitem>
+            <para>Authentication Domain</para>
+
+            <para>Your server and the clients must be part of a security
+            association where identity data is coming from a common source.
+            ACLs in Darwin are based on UUIDs and so is the ACL specification
+            in AFP 3.2. Therefor your source of identity data has to provide
+            an attribute for every user and group where a UUID is stored as a
+            ASCII string. In other words:</para>
+
+            <itemizedlist>
+              <listitem>
+                <para>you need an Open Directory Server or an LDAP server
+                where you store UUIDs in some attribute</para>
+              </listitem>
+
+              <listitem>
+                <para>your clients must be configured to use this
+                server</para>
+              </listitem>
+
+              <listitem>
+                <para>your server should be configured to use this server via
+                nsswitch and PAM</para>
+              </listitem>
+
+              <listitem>
+                <para>configure Netatalk via the special <link
+                linkend="acl_options">LDAP options for ACLs</link> in <link
+                linkend="afp.conf.5">afp.conf</link> so that Netatalk is able
+                to retrieve the UUID for users and groups via LDAP search
+                queries</para>
+              </listitem>
+            </itemizedlist>
+          </listitem>
+        </orderedlist>
+      </sect3>
+
+      <sect3>
+        <title>OS X ACLs</title>
+
+        <para>With Access Control Lists (ACLs) Mac OS X offers a powerful
+        extension of the traditional UNIX permissions model. An ACL is an
+        ordered list of Access Control Entries (ACEs) explicitly granting or
+        denying a set of permissions to a given user or group.</para>
+
+        <para>Unlike UNIX permissions, which are bound to user or group IDs,
+        ACLs are tied to UUIDs. For this reason accessing an object's ACL
+        requires server and client to use a common directory service which
+        translates between UUIDs and user/group IDs.</para>
+
+        <para>ACLs and UNIX permissions interact in a rather simple way. As
+        ACLs are optional UNIX permissions act as a default mechanism for
+        access control. Changing an objects's UNIX permissions will leave it's
+        ACL intact and modifying an ACL will never change the object's UNIX
+        permissions. While doing access checks, OS X first examines an
+        object's ACL evaluating ACEs in order until all requested rights have
+        been granted, a requested right has been explicitly denied by an ACE
+        or the end of the list has been reached. In case there is no ACL or
+        the permissions granted by the ACL are not sufficient to fulfill the
+        request, OS X next evaluates the object's UNIX permissions. Therefore
+        ACLs always have precedence over UNIX permissions.</para>
+      </sect3>
+
+      <sect3>
+        <title>ZFS ACLs</title>
+
+        <para>ZFS ACLs closely match OS X ACLs. Both offer mostly identical
+        fine grained permissions and inheritance settings.</para>
+      </sect3>
+
+      <sect3>
+        <title>POSIX ACLs</title>
+
+        <sect4>
+          <title>Overview</title>
+
+          <para>Compared to OS X or NFSv4 ACLs, Posix ACLs represent a
+          different, less versatile approach to overcome the limitations of
+          the traditional UNIX permissions. Implementations are based on the
+          withdrawn Posix 1003.1e standard.</para>
+
+          <para>The standard defines two types of ACLs. Files and directories
+          can have access ACLs which are consulted for access checks.
+          Directories can also have default ACLs irrelevant to access checks.
+          When a new object is created inside a directory with a default ACL,
+          the default ACL is applied to the new object as it's access ACL.
+          Subdirectories inherit default ACLs from their parent. There are no
+          further mechanisms of inheritance control. </para>
+
+          <para>Architectural differences between Posix ACLs and OS X ACLs
+          especially involve:</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>No fine-granular permissions model. Like UNIX
+                permissions Posix ACLs only differentiate between read, write
+                and execute permissions.</para>
+              </listitem>
+
+              <listitem>
+                <para>Entries within an ACL are unordered.</para>
+              </listitem>
+
+              <listitem>
+                <para>Posix ACLs can only grant rights. There is no way to
+                explicitly deny rights by an entry.</para>
+              </listitem>
+
+              <listitem>
+                <para>UNIX permissions are integrated into an ACL as special
+                entries.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>Posix 1003.1e defines 6 different types of ACL entries. The
+          first three types are used to integrate standard UNIX permissions.
+          They form a minimal ACL, their presence is mandatory and only one
+          entry of each type is allowed within an ACL.</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>ACL_USER_OBJ: the owner's access rights.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_GROUP_OBJ: the owning group's access rights.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_OTHER: everybody's access rights.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>The remaining entry types expand the traditional permissions
+          model:</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>ACL_USER: grants access rights to a certain user.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_GROUP: grants access rights to a certain
+                group.</para>
+              </listitem>
+
+              <listitem>
+                <para>ACL_MASK: limits the maximum access rights which can be
+                granted by entries of type ACL_GROUP_OBJ, ACL_USER and
+                ACL_GROUP. As the name suggests, this entry acts as a mask.
+                Only one ACL_MASK entry is allowed per ACL. If an ACL contains
+                ACL_USER or ACL_GROUP entries, an ACL_MASK entry must be
+                present too, otherwise it is optional.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>In order to maintain compatibility with applications not aware
+          of ACLs, Posix 1003.1e changes the semantics of system calls and
+          utilities which retrieve or manipulate an objects UNIX permissions.
+          In case an object only has a minimal ACL, the group permissions bits
+          of the UNIX permissions correspond to the value of the ACL_GROUP_OBJ
+          entry.</para>
+
+          <para>However, if the ACL also contains an ACL_MASK entry, the
+          behavior of those system calls and utilities is different. The group
+          permissions bits of the UNIX permissions correspond to the value of
+          the ACL_MASK entry, i. e. calling "chmod g-w" will not only revoke
+          write access for the group, but for all entities which have been
+          granted write access by ACL_USER or ACL_GROUP entries.</para>
+        </sect4>
+
+        <sect4>
+          <title>Mapping POSIX ACLs to OS X ACLs</title>
+
+          <para>When a client wants to read an object's ACL, afpd maps it's
+          Posix ACL onto an equivalent OS X ACL. Writing an object's ACL
+          requires afpd to map an OS X ACL onto a Posix ACL. Due to
+          architectural restrictions of Posix ACLs, it is usually impossible
+          to find an exact mapping so that the result of the mapping process
+          will be an approximation of the original ACL's semantic.</para>
+
+          <para><itemizedlist>
+              <listitem>
+                <para>afpd silently discard entries which deny a set of
+                permissions because they they can't be represented within the
+                Posix architecture. </para>
+              </listitem>
+
+              <listitem>
+                <para>As entries within Posix ACLs are unordered, it is
+                impossible to preserve order.</para>
+              </listitem>
+
+              <listitem>
+                <para>Inheritance control is subject to severe limitations as
+                well:<itemizedlist>
+                    <listitem>
+                      <para>Entries with the only_inherit flag set will only
+                      become part of the directory's default ACL.</para>
+                    </listitem>
+
+                    <listitem>
+                      <para>Entries with at least one of the flags
+                      file_inherit, directory_inherit or limit_inherit set,
+                      will become part of the directory's access and default
+                      ACL, but the restrictions they impose on inheritance
+                      will be ignored.</para>
+                    </listitem>
+                  </itemizedlist></para>
+              </listitem>
+
+              <listitem>
+                <para>The lack of a fine-granular permission model on the
+                Posix side will normally result in an increase of granted
+                permissions.</para>
+              </listitem>
+            </itemizedlist></para>
+
+          <para>As OS X clients aren't aware of the Posix 1003.1e specific
+          relationship between UNIX permissions and ACL_MASK, afpd does not
+          expose this feature to the client to avoid compatibility issues and
+          handles *unix permissions and ACLs the same way as Apple's reference
+          implementation of AFP does. When an object's UNIX permissions are
+          requested, afpd calculates proper group rights and returns the
+          result together with the owner's and everybody's access rights to
+          the caller via "permissions" and "ua_permissions" members of the
+          FPUnixPrivs structure (see Apple Filing Protocol Reference, page
+          181). Changing an object's permissions, afpd always updates
+          ACL_USER_OBJ, ACL_GROUP_OBJ and ACL_OTHERS. If an ACL_MASK entry is
+          present too, afpd recalculates it's value so that the new group
+          rights become effective and existing entries of type ACL_USER or
+          ACL_GROUP stay intact.</para>
+        </sect4>
+      </sect3>
+    </sect2>
+
+    <sect2 id="fce">
+      <title>Filesystem Change Events<indexterm>
+          <primary>FCE</primary>
+        </indexterm></title>
+
+      <para>Netatalk includes a nifty filesystem change event mechanism where
+      afpd processes notfiy interested listeners about certain filesystem
+      event by UDP network datagrams.</para>
+
+      <para>For the format of the UDP packets and for an example C application
+      that demonstrates how to use these in a listener, take a look at the
+      Netatalk sourcefile <filename>bin/misc/fce.c</filename>.</para>
+
+      <para>The currently supported FCE events are<itemizedlist>
+          <listitem>
+            <para>file modification (fmod)</para>
+          </listitem>
+
+          <listitem>
+            <para>file deletion (fdel)</para>
+          </listitem>
+
+          <listitem>
+            <para>directory deletion (ddel)</para>
+          </listitem>
+
+          <listitem>
+            <para>file creation (fcre)</para>
+          </listitem>
+
+          <listitem>
+            <para>directory deletion (ddel)</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>For details on the available simple configuration options take a
+      look at <filename><link
+      linkend="fceconf">afp.conf</link></filename>.</para>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Starting and stopping Netatalk</title>
+
+    <para>The Netatalk distribution comes with several operating system
+    specific startup script templates that are tailored according to the
+    options given to the "configure" script before compiling. Currently,
+    templates are provided for RedHat (sysv style), RedHat (systemd style),
+    SUSE (sysv style), SUSE (systemd style), Gentoo, NetBSD, Debian and
+    Solaris. You can select to install the generated startup script(s)
+    <indexterm>
+        <primary>Startscript</primary>
+
+        <secondary>startup script</secondary>
+      </indexterm> by specifying a system type to "configure". To
+    automatically install startup scripts give one of the available
+    <option>--with-init-style</option> option to "configure".</para>
+
+    <para>Since new releases of Linux distributions appear all the time and
+    the startup procedure for the other systems mentioned above might change
+    as well, it is probably a good idea to not blindly install a startup
+    script but to look at it first to see if it will work on your system. If
+    you use Netatalk as part of a fixed setup, like a Linux distribution, an
+    RPM or a BSD package, things will probably have been arranged properly for
+    you. The following therefore applies mostly for people who have compiled
+    Netatalk themselves.</para>
+
+    <para>The following daemon need to be started by whatever startup script
+    mechanism is used:</para>
+
+    <itemizedlist>
+      <listitem>
+        <para>netatalk<indexterm>
+            <primary>netatalk</primary>
+          </indexterm></para>
+      </listitem>
+    </itemizedlist>
+
+    <para>Additionally, make sure that the configuration file
+    <filename>afp.conf</filename> is in the right place.</para>
+  </sect1>
+</chapter>
diff --git a/doc/manual/install.xml b/doc/manual/install.xml
new file mode 100644 (file)
index 0000000..a64b384
--- /dev/null
@@ -0,0 +1,347 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="installation">
+  <chapterinfo>
+    <date>24.8.2012</date>
+  </chapterinfo>
+
+  <title>Installation</title>
+
+  <warning>
+    <para>If you have previously used an older version of Netatalk, please
+    read the chapter about <link linkend="upgrade">upgrading</link> first
+    !!!</para>
+  </warning>
+
+  <para></para>
+
+  <sect1>
+    <title>How to obtain Netatalk</title>
+
+    <para>Please have a look at the netatalk page on sourceforge for the most
+    recent informations on this issue.</para>
+
+    <para><ulink
+    url="http://sourceforge.net/projects/netatalk/">http://sourceforge.net/projects/netatalk/</ulink></para>
+
+    <sect2>
+      <title>Binary packages</title>
+
+      <para>Binary packages of Netatalk are included in some Linux and UNIX
+      distributions. You might want to have a look at the usual locations,
+      too.</para>
+
+      <para>Ubuntu package: <ulink
+      url="https://launchpad.net/ubuntu">https://launchpad.net/ubuntu
+      </ulink></para>
+
+      <para>Debian package: <ulink
+      url="http://packages.debian.org/">http://packages.debian.org/
+      </ulink></para>
+
+      <para>various RPM package: <ulink
+      url="http://rpmfind.net/">http://rpmfind.net/ </ulink></para>
+
+      <para>Fedora/RHEL package: <ulink
+      url="http://koji.fedoraproject.org/koji/search">http://koji.fedoraproject.org/koji/search
+      </ulink></para>
+
+      <para>Gentoo package: <ulink
+      url="http://packages.gentoo.org/">http://packages.gentoo.org/
+      </ulink></para>
+
+      <para>openSUSE package: <ulink
+      url="http://software.opensuse.org/">http://software.opensuse.org/
+      </ulink></para>
+
+      <para>Solaris package: <ulink
+      url="http://www.blastwave.org/">http://www.blastwave.org/
+      </ulink></para>
+
+      <para>FreeBSD ports: <ulink
+      url="http://www.freebsd.org/ports/index.html">http://www.freebsd.org/ports/index.html
+      </ulink></para>
+
+      <para>NetBSD pkgsrc: <ulink
+      url="http://pkgsrc.se/search.php">http://pkgsrc.se/search.php
+      </ulink></para>
+
+      <para>OpenBSD ports:<ulink
+      url="http://openports.se/search.php">http://openports.se/search.php
+      </ulink></para>
+
+      <para>etc.<indexterm>
+          <primary>RPM</primary>
+
+          <secondary>Red Hat Package Manager package</secondary>
+        </indexterm><indexterm>
+          <primary>Deb</primary>
+
+          <secondary>Debian package</secondary>
+        </indexterm><indexterm>
+          <primary>Ports</primary>
+
+          <secondary>FreeBSD port</secondary>
+        </indexterm></para>
+    </sect2>
+
+    <sect2>
+      <title>Source packages</title>
+
+      <sect3>
+        <title>Tarballs</title>
+
+        <para>Prepacked tarballs in .tar.gz and tar.bz2 format are available
+        on the netatalk page on <ulink
+        url="http://netatalk.sourceforge.net/">sourceforge</ulink>.</para>
+      </sect3>
+
+      <sect3>
+        <title>Git</title>
+
+        <para>Downloading the Git repository can be done quickly and
+        easily.</para>
+
+        <orderedlist>
+          <listitem>
+            <para>Make sure you have Git installed. <command>which
+            git</command> should produce a path to git.</para>
+
+            <screen><prompt>$&gt;</prompt> <userinput>which git</userinput>
+<computeroutput>/usr/bin/git</computeroutput></screen>
+          </listitem>
+
+          <listitem>
+            <para>If you don't have one make a source directory.
+            <command>cd</command> to this directory.</para>
+
+            <screen><prompt>$&gt;</prompt> <userinput>mkdir /path/to/new/source/dir</userinput>
+<prompt>$&gt;</prompt> <userinput>cd /path/to/new/source/dir</userinput></screen>
+          </listitem>
+
+          <listitem>
+            <para>Now get the source:</para>
+
+            <screen><prompt>$&gt;</prompt> <userinput>git clone git://git.code.sf.net/p/netatalk/code netatalk-code
+</userinput><computeroutput>Initialized empty Git repository in /path/to/new/source/dir/netatalk/.git/
+remote: Counting objects: 2503, done.
+...
+</computeroutput></screen>
+
+            <para>This will create a local directory called "netatalk-code"
+            containing a complete and fresh copy of the whole Netatalk source
+            from the Git repository.</para>
+          </listitem>
+
+          <listitem>
+            <para>In order to keep your repository copy updated, occasionally
+            run:</para>
+
+            <screen><prompt>$&gt;</prompt> <userinput>git pull</userinput></screen>
+          </listitem>
+
+          <listitem>
+            <para>Now <command>cd</command> to the netatalk directory and run
+            <command>./bootstrap</command>. This will create the
+            <filename>configure</filename> script required in the next
+            step.</para>
+
+            <screen><prompt>$&gt;</prompt> <userinput>./bootstrap</userinput></screen>
+          </listitem>
+        </orderedlist>
+
+        <para></para>
+      </sect3>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Compiling Netatalk</title>
+
+    <sect2>
+      <title>Prerequisites</title>
+
+      <sect3>
+        <title>Required third party software</title>
+
+        <itemizedlist>
+          <listitem>
+            <para>Berkeley DB<indexterm>
+                <primary>BDB</primary>
+
+                <secondary>Berkeley DB</secondary>
+              </indexterm>.</para>
+
+            <para>At the time of writing, the following versions are
+            supported:</para>
+
+            <itemizedlist>
+              <listitem>
+                <para>minimum 4.6.x</para>
+              </listitem>
+            </itemizedlist>
+
+            <para>In case Berkeley DB is not installed on your system, please
+            download it from:</para>
+
+            <para><ulink
+            url="http://www.oracle.com/technetwork/products/berkeleydb/downloads/index.html">
+            http://www.oracle.com/technetwork/products/berkeleydb/downloads/index.html</ulink></para>
+
+            <para>and follow the <link linkend="build-bdb">installation
+            instructions</link>.</para>
+          </listitem>
+
+          <listitem>
+            <para>Libgcrypt</para>
+
+            <para>Required for OS X 10.7 and later. Libgcrypt is needed for
+            DHX2.</para>
+
+            <para>Libgcrypt can be downloaded from: <ulink
+            url="http://directory.fsf.org/wiki/Libgcrypt">
+            http://directory.fsf.org/wiki/Libgcrypt</ulink>.</para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+
+      <sect3>
+        <title>Optional third party software</title>
+
+        <para>Netatalk can use the following third party software to enhance
+        it's functionality.</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>mDNSresponderPOSIX or Avahi for Bonjour (aka
+            Zeroconf)</para>
+
+            <para>Mac OS X 10.2 and later use Bonjour (aka Zeroconf) for
+            service discovery.</para>
+
+            <para>Avahi must be build with DBUS support (
+            <userinput>--enable-dbus</userinput>).</para>
+
+            <para>You can download Avahi from: <ulink
+            url="http://www.avahi.org/">http://www.avahi.org/</ulink>.</para>
+
+            <para>You can download mDNSresponder from: <ulink
+            url="http://opensource.apple.com/tarballs/mDNSResponder/">http://opensource.apple.com/tarballs/mDNSResponder/</ulink>.</para>
+          </listitem>
+
+          <listitem>
+            <para>TCP wrappers</para>
+
+            <para>Wietse Venema's network logger, also known as TCPD or
+            LOG_TCP.</para>
+
+            <para>Security options are: access control per host, domain and/or
+            service; detection of host name spoofing or host address spoofing;
+            booby traps to implement an early-warning system.</para>
+
+            <para>TCP Wrappers can be downloaded from: <ulink
+            url="ftp://ftp.porcupine.org/pub/security">ftp://ftp.porcupine.org/pub/security</ulink>/</para>
+          </listitem>
+
+          <listitem>
+            <para>PAM<indexterm>
+                <primary>PAM</primary>
+
+                <secondary>Pluggable Authentication Modules</secondary>
+              </indexterm></para>
+
+            <para>PAM provides a flexible mechanism for authenticating users.
+            PAM was invented by SUN<indexterm>
+                <primary>SUN</primary>
+
+                <secondary>Sun Microsystems</secondary>
+              </indexterm> Microsystems. Linux-PAM is a suite of shared
+            libraries that enable the local system administrator to choose how
+            applications authenticate users.</para>
+
+            <para>You can get the Linux PAM documentation and sources from
+            <ulink
+            url="http://www.kernel.org/pub/linux/libs/pam/">http://www.kernel.org/pub/linux/libs/pam/</ulink>.</para>
+          </listitem>
+
+          <listitem>
+            <para>iconv</para>
+
+            <para>iconv provides conversion routines for many character
+            encodings. Netatalk uses it to provide charsets it does not have
+            built in conversions for, like ISO-8859-1. On glibc systems,
+            Netatalk can use the glibc provided iconv implementation.
+            Otherwise you can use the GNU libiconv implementation.</para>
+
+            <para>You can download GNU libiconv from: <olink><ulink
+            url="http://www.gnu.org/software/libiconv/">http://www.gnu.org/software/libiconv/</ulink></olink>.</para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+    </sect2>
+
+    <sect2 id="compiling-netatalk">
+      <title>Compiling<indexterm>
+          <primary>Compile</primary>
+
+          <secondary>Compiling Netatalk from Source</secondary>
+        </indexterm> Netatalk</title>
+
+      <sect3>
+        <title>Configuring the build</title>
+
+        <para>To build the binaries, first run the program
+        <command>./configure</command> in the source directory. This should
+        automatically configure Netatalk for your operating system. If you
+        have unusual needs, then you may wish to run</para>
+
+        <screen>$&gt; <userinput>./configure --help</userinput></screen>
+
+        <para>to see what special options you can enable.</para>
+
+        <para>The most used configure options are:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para><option>--with-init-style</option>=redhat-sysv|redhat-systemd|suse-sysv|suse-systemd|gentoo|netbsd|debian|solaris|systemd</para>
+
+            <para>This option helps netatalk to determine where to install the
+            start scripts.</para>
+          </listitem>
+
+          <listitem>
+            <para><option>--with-bdb</option>=<replaceable>/path/to/bdb/installation/</replaceable></para>
+
+            <para>In case you installed Berkeley DB in a non-standard
+            location, you will <emphasis>have</emphasis> to give the install
+            location to netatalk, using this switch.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>Now run configure with any options you need</para>
+
+        <screen><prompt>$&gt;</prompt> <userinput>./configure [arguments]</userinput></screen>
+
+        <para>Configure will end up in an overview showing the settings the
+        Netatalk Makefiles have been created with.</para>
+
+        <para>If this step fails please visit the <ulink
+        url="http://netatalk.sourceforge.net/wiki/index.php/Troubleshooting">troubleshooting
+        guide</ulink>.</para>
+
+        <para>Next, running</para>
+
+        <screen><prompt>$&gt;</prompt> <userinput>make</userinput></screen>
+
+        <para>should produce the Netatalk binaries (this step can take several
+        minutes to complete).</para>
+
+        <para>When the process finished you can use</para>
+
+        <screen><prompt>$&gt;</prompt> <userinput>make install</userinput></screen>
+
+        <para>to install the binaries and documentation (must be done as
+        "root" when using default locations).</para>
+      </sect3>
+    </sect2>
+  </sect1>
+</chapter>
diff --git a/doc/manual/intro.xml b/doc/manual/intro.xml
new file mode 100644 (file)
index 0000000..0b70a9a
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="intro">
+  <title>Introduction to Netatalk</title>
+
+  <para>Netatalk is an OpenSource software package, that can be used to turn
+  a *NIX machine into an extremely high-performance and
+  reliable file server for Macintosh computers.</para>
+
+  <para>Using Netatalk's AFP 3.3 compliant file-server leads to significantly
+  higher transmission speeds compared with Macs accessing a server via
+  SaMBa/NFS while providing clients with the best possible user experience
+  (full support for Macintosh metadata, flawlessly supporting mixed
+  environments of classic Mac OS and OS X clients)</para>
+
+</chapter>
diff --git a/doc/manual/manual.xml.in b/doc/manual/manual.xml.in
new file mode 100644 (file)
index 0000000..eab6e62
--- /dev/null
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+
+<!ENTITY Intro SYSTEM "netatalk/intro.xml">
+<!ENTITY Install SYSTEM "netatalk/install.xml">
+<!ENTITY Upgrade SYSTEM "netatalk/upgrade.xml">
+<!ENTITY Configuration SYSTEM "netatalk/configuration.xml">
+
+<!ENTITY ad.1 SYSTEM "man/man1/ad.1.xml">
+<!ENTITY afpd.8 SYSTEM "man/man8/afpd.8.xml">
+<!ENTITY cnid_dbd.8 SYSTEM "man/man8/cnid_dbd.8.xml">
+<!ENTITY cnid_metad.8 SYSTEM "man/man8/cnid_metad.8.xml">
+<!ENTITY afp.conf.5 SYSTEM "man/man5/afp.conf.5.xml">
+<!ENTITY afp_signature.conf.5 SYSTEM "man/man5/afp_signature.conf.5.xml">
+<!ENTITY afp_voluuid.conf.5 SYSTEM "man/man5/afp_voluuid.conf.5.xml">
+<!ENTITY afpldaptest.1 SYSTEM "man/man1/afpldaptest.1.xml">
+<!ENTITY afppasswd.1 SYSTEM "man/man1/afppasswd.1.xml">
+<!ENTITY afpstats.1 SYSTEM "man/man1/afpstats.1.xml">
+<!ENTITY apple_dump.1 SYSTEM "man/man1/apple_dump.1.xml">
+<!ENTITY extmap.conf.5 SYSTEM "man/man5/extmap.conf.5.xml">
+<!ENTITY macusers.1 SYSTEM "man/man1/macusers.1.xml">
+<!ENTITY megatron.1 SYSTEM "man/man1/megatron.1.xml">
+<!ENTITY netatalk.8 SYSTEM "man/man8/netatalk.8.xml">
+<!ENTITY netatalk-config.1 SYSTEM "man/man1/netatalk-config.1.xml">
+<!ENTITY uniconv.1 SYSTEM "man/man1/uniconv.1.xml">
+<!ENTITY asip-status.pl.1 SYSTEM "man/man1/asip-status.pl.1.xml">
+<!ENTITY dbd.1 SYSTEM "man/man1/dbd.1.xml">
+]>
+<book id="netatalk-manual">
+  <title>Netatalk 3.0 Manual</title>
+  
+  <bookinfo>
+    <date>03-24-2013</date>
+    <releaseinfo>@NETATALK_VERSION@</releaseinfo>
+  </bookinfo>
+
+ <?latex \setcounter{page}{3} ?>
+<preface>
+        <title>Legal Notice</title>
+<para>
+This documentation is distributed under the GNU General Public License (GPL) version 2.  
+A copy of the license is included in this documentation, as well as within the Netatalk source
+distribution.  An on-line copy can be found at <ulink
+url="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</ulink>
+</para>
+</preface>
+
+  <?latex \cleardoublepage ?>
+  <!-- Contents -->
+  <toc/>
+  <?latex \cleardoublepage ?>
+  <?latex \pagenumbering{arabic} ?>
+
+  &Intro;
+  <?latex \cleardoublepage ?>
+
+  &Install;
+  <?latex \cleardoublepage ?>
+
+  &Configuration;
+  <?latex \cleardoublepage ?>
+
+  &Upgrade;
+  <?latex \cleardoublepage ?>
+
+  <chapter id="man-pages">
+    <title>Manual Pages</title>
+
+    <para>This is a collection of the man pages delivered with Netatalk.</para>
+
+    &ad.1;
+
+    &afp.conf.5;
+
+    &afp_signature.conf.5;
+
+    &afp_voluuid.conf.5;
+
+    &afpd.8;
+
+    &afpldaptest.1;
+
+    &afppasswd.1;
+
+    &afpstats.1;
+
+    &apple_dump.1;
+
+    &asip-status.pl.1;
+
+    &cnid_dbd.8;
+
+    &cnid_metad.8;
+
+    &dbd.1;
+
+    &extmap.conf.5;
+
+    &macusers.1;
+
+    &megatron.1;
+
+    &netatalk.8;
+
+    &netatalk-config.1;
+
+    &uniconv.1;
+  </chapter>
+  <?latex \cleardoublepage ?>
+
+  <?latex \include{gpl}?>
+  <?latex \cleardoublepage ?>
+
+  <index id="manual-index"><title>Index</title></index>
+</book>
diff --git a/doc/manual/upgrade.xml b/doc/manual/upgrade.xml
new file mode 100644 (file)
index 0000000..ebd1963
--- /dev/null
@@ -0,0 +1,1555 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="upgrade">
+  <chapterinfo>
+    <date>2.15.2013</date>
+
+    <author>
+      <firstname>Frank</firstname>
+
+      <surname>Lahm</surname>
+    </author>
+
+    <pubdate>15 Feb, 2013</pubdate>
+  </chapterinfo>
+
+  <title>Upgrading from Netatalk 2</title>
+
+  <sect1>
+    <title>Overview</title>
+
+    <para>There are two major changes in Netatalk:<orderedlist>
+        <listitem>
+          <para>New configuration file <filename><link
+          linkend="afp.conf.5">afp.conf</link></filename>, obsoleting all
+          previous configuration files</para>
+        </listitem>
+
+        <listitem>
+          <para>New AppleDouble backend "<option>appledouble = ea</option>"
+          which stores Mac metadata and resource forks in extended attributes
+          of the filesystem</para>
+        </listitem>
+      </orderedlist></para>
+
+    <sect2>
+      <title>New configuration</title>
+
+      <para><itemizedlist>
+          <listitem>
+            <para>ini style syntax (like Samba’s smb.conf)</para>
+          </listitem>
+
+          <listitem>
+            <para>one to rule them all: configure AFP settings and volumes in
+            one file</para>
+          </listitem>
+
+          <listitem>
+            <para>obsoletes <filename>afpd.conf</filename>,
+            <filename>netatalk.conf</filename>,
+            <filename>AppleVolumes.default</filename> and
+            <filename>afp_ldap.conf</filename></para>
+          </listitem>
+        </itemizedlist><warning>
+          <para>most option names have changed, read the full manpage <link
+          linkend="afp.conf.5">afp.conf</link> for details</para>
+        </warning></para>
+    </sect2>
+
+    <sect2>
+      <title>New AppleDouble backend</title>
+
+      <para>New AppleDouble backend "<option>appledouble = ea</option>" which
+      stores Mac metadata and resource forks in extended attributes of the
+      filesystem.<itemizedlist>
+          <listitem>
+            <para>default backend (!)</para>
+          </listitem>
+
+          <listitem>
+            <para>requires a filesystem with Extended Attributes, fallback is
+            "<option>appledouble = v2</option>"</para>
+          </listitem>
+
+          <listitem>
+            <para>converts filesystems from "<option>appledouble = v2</option>"
+            to "<option>appledouble = ea</option>" on the fly when accessed
+            (can be disabled)</para>
+          </listitem>
+
+          <listitem>
+            <para><command><link linkend="dbd.1">dbd</link></command> can be
+            used to do conversion in one shot</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>Implementation details:<itemizedlist>
+          <listitem>
+            <para>stores Mac Metadata (eg FinderInfo, AFP Flags, Comment,
+            CNID) in an Extended Attributed named
+            “<filename>org.netatalk.Metadata</filename>”</para>
+          </listitem>
+
+          <listitem>
+            <para>stores Mac ResourceFork either in<itemizedlist>
+                <listitem>
+                  <para>an Extended Attribute named
+                  “<filename>org.netatalk.ResourceFork</filename>”
+                  on Solaris (FreeBSD?) w. ZFS, or in</para>
+                </listitem>
+
+                <listitem>
+                  <para>an extra AppleDouble file named “<filename>._file</filename>” for a file
+                  named “<filename>file</filename>”</para>
+                </listitem>
+              </itemizedlist></para>
+          </listitem>
+
+          <listitem>
+            <para>the format of the ._ file is exactly as the Mac’s CIFS
+            client expects it when accessing the same filesystem via a CIFS
+            server (Samba), thus you can have parallel access from Macs to the
+            same dataset via AFP and CIFS without the risk of loosing data
+            (resources or metadata). Accessing the same dataset with CIFS
+            from Windows clients will still break the coupling of
+            “<filename>file</filename>” and “<filename>._file</filename>”
+            on non ZFS filesystems (see above), so for this we still
+            need an enhanced Samba VFS module (in the works).</para>
+          </listitem>
+        </itemizedlist></para>
+
+      <para>As these days the only applications making use of Resource Forks
+      are Adobe Photoshop (image preview) and Postscript Type 1 fonts, even on
+      eg Linux you’ll get rid of 99% of any extra Netatalk AppleDouble files
+      (and folders).</para>
+    </sect2>
+
+    <sect2>
+      <title>Other major changes</title>
+
+      <para><itemizedlist>
+          <listitem>
+            <para>New service controller daemon <link
+            linkend="netatalk.8">netatalk</link> which is responsible for
+            starting and restarting the AFP and CNID daemons. All bundled
+            start scripts have been updated, make sure to update yours!</para>
+          </listitem>
+
+          <listitem>
+            <para>The CNID databases are now stored under
+              <filename>/var/netatalk/CNID/</filename>
+              by default. You can use configure --localstatedir=PATH at
+              compile time to change the location.</para>
+          </listitem>
+
+          <listitem>
+            <para>Netatalk 2.x volume options “usedots” and “upriv” now
+            enabled by default</para>
+          </listitem>
+
+          <listitem>
+            <para>Removed SLP and AFP proxy support</para>
+          </listitem>
+
+          <listitem>
+            <para>Removed type/creator extension mapping
+            support</para>
+          </listitem>
+        </itemizedlist></para>
+    </sect2>
+  </sect1>
+
+  <sect1>
+    <title>Upgrading</title>
+
+    <para><orderedlist>
+        <listitem>
+          <para>Stop Netatalk 2.x</para>
+        </listitem>
+
+        <listitem>
+          <para>Install Netatalk 3</para>
+        </listitem>
+
+        <listitem>
+          <para>Manually recreate configuration in
+          <option>afp.conf</option> and <option>extmap.conf</option></para>
+        </listitem>
+
+        <listitem>
+          <para>Update your Netatalk start script (SMF, systemd, whatever...)
+          to only start <link linkend="netatalk.8">netatalk</link></para>
+        </listitem>
+
+        <listitem>
+          <para>Move <filename>afp_voluuid.conf</filename> and
+          <filename>afp_signature.conf</filename> to the localstate directory (default
+          <filename>/var/netatalk/</filename>), you can use <command>afpd -v</command>
+          in order to find the correct path</para>
+        </listitem>
+
+        <listitem>
+          <para>Start Netatalk 3</para>
+        </listitem>
+      </orderedlist></para>
+  </sect1>
+
+  <sect1>
+    <title>Notes</title>
+
+    <itemizedlist>
+      <listitem>
+        <para>Solaris ZFS permissions</para>
+
+        <para>On Solaris with ZFS you have to make sure users have
+        filesystem permissions to read, create, modify (default: yes) and
+        delete (default: no) extended attributes.</para>
+
+        <para>To grant this right to a group “staff” you’d use this
+        command:</para>
+
+        <para><command>pfexec chmod A+group:staff:RW:fd:allow
+        /Volumes/test/</command></para>
+
+        <para>Remember to run this once before you share a volume so that
+        this permission inherits appropiately (fd flags in above
+        command).</para>
+      </listitem>
+    </itemizedlist>
+  </sect1>
+  <sect1>
+    <title>Table with old and new configuration file names</title>
+    <para><table frame="all">
+        <title>old and new configuration file names</title>
+        <tgroup cols="3">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old File Name</entry>
+              <entry>New File Name</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>-</entry>
+              <entry><filename>etc/afp.conf</filename></entry>
+              <entry>new ini-style format</entry>
+            </row>
+            <row>
+              <entry>-</entry>
+              <entry><filename>etc/extmap.conf</filename></entry>
+              <entry>starting with netatalk 3.0.2</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_signature.conf</filename></entry>
+              <entry><filename>var/netatalk/afp_signature.conf</filename></entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_voluuid.conf</filename></entry>
+              <entry><filename>var/netatalk/afp_voluuid.conf</filename></entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/netatalk.conf</filename>
+              (<filename>/etc/default/netatalk</filename>)</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afpd.conf</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/afp_ldap.conf</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/AppleVolumes.default</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>etc/netatalk/AppleVolumes.system</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry><filename>~/.AppleVolumes</filename></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table></para>
+  </sect1>
+
+  <sect1>
+    <title>Table with old and new option names</title>
+    <para><table frame="all">
+        <title>from netatalk.conf (/etc/default/netatalk) to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old netatalk.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Default Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>ATALK_NAME</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>ATALK_UNIX_CHARSET</entry>
+              <entry>unix charset</entry>
+              <entry><emphasis role="bold">LOCALE</emphasis></entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ATALK_MAC_CHARSET</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_METAD_RUN</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>controlled by netatalk(8)</entry>
+            </row>
+            <row>
+              <entry>AFPD_RUN</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>controlled by netatalk(8)</entry>
+            </row>
+            <row>
+              <entry>AFPD_MAX_CLIENTS</entry>
+              <entry>max connections</entry>
+              <entry><emphasis role="bold">20</emphasis></entry>
+              <entry><emphasis role="bold">200</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>AFPD_UAMLIST</entry>
+              <entry>uam list</entry>
+              <entry>-U uams_dhx.so,uams_dhx2.so</entry>
+              <entry>uams_dhx.so uams_dhx2.so</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>AFPD_GUEST</entry>
+              <entry>guest account</entry>
+              <entry>nobody</entry>
+              <entry>nobody</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_CONFIG</entry>
+              <entry>log level</entry>
+              <entry>-l log_note</entry>
+              <entry>cnid:note</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>CNID_CONFIG</entry>
+              <entry>log file</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ATALKD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>PAPD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>TIMELORD_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>A2BOOT_RUN</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>ATALK_BGROUND</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>ATALK_ZONE</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from afpd.conf to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old afpd.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Default Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>1st field ("-" or "server name")</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>-uamlist</entry>
+              <entry>uam list</entry>
+              <entry>-U uams_dhx.so,uams_dhx2.so</entry>
+              <entry>uams_dhx.so uams_dhx2.so</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-nozeroconf</entry>
+              <entry>zeroconf</entry>
+              <entry>-</entry>
+              <entry>yes (if supported)</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-advertise_ssh</entry>
+              <entry>advertise ssh</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]savepassword</entry>
+              <entry>save password</entry>
+              <entry>-savepassword</entry>
+              <entry>yes</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]setpassword</entry>
+              <entry>set password</entry>
+              <entry>-nosetpassword</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-client_polling</entry>
+              <entry>client polling</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-hostname</entry>
+              <entry>hostname</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>use gethostname() by default</entry>
+            </row>
+            <row>
+              <entry>-loginmesg</entry>
+              <entry>login message</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-guestname</entry>
+              <entry>guest account</entry>
+              <entry>nobody</entry>
+              <entry>nobody</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-passwdfile</entry>
+              <entry>passwd file</entry>
+              <entry>afppasswd</entry>
+              <entry>afppasswd</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-passwdminlen</entry>
+              <entry>passwd minlen</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-tickleval</entry>
+              <entry>tickleval</entry>
+              <entry>30</entry>
+              <entry>30</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-timeout</entry>
+              <entry>timeout</entry>
+              <entry>4</entry>
+              <entry>4</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-sleep</entry>
+              <entry>sleep time</entry>
+              <entry>10</entry>
+              <entry>10</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-dsireadbuf</entry>
+              <entry>dsireadbuf</entry>
+              <entry>12</entry>
+              <entry>12</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-server_quantum</entry>
+              <entry>server quantum</entry>
+              <entry>303840</entry>
+              <entry>303840</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-volnamelen</entry>
+              <entry>volnamelen</entry>
+              <entry>80</entry>
+              <entry>80</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-setuplog</entry>
+              <entry>log level</entry>
+              <entry>default log_note</entry>
+              <entry>default:note</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-setuplog</entry>
+              <entry>log file</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-admingroup</entry>
+              <entry>admingroup</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5service</entry>
+              <entry>k5 service</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5realm</entry>
+              <entry>k5 realm</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-k5keytab</entry>
+              <entry>k5 keytab</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-uampath</entry>
+              <entry>uam path</entry>
+              <entry><emphasis role="bold">etc/netatalk/uams/</emphasis></entry>
+              <entry><emphasis role="bold">lib/netatalk/</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>moved to $libdir</entry>
+            </row>
+            <row>
+              <entry>-ipaddr</entry>
+              <entry>afp listen</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-cnidserver</entry>
+              <entry>cnid server</entry>
+              <entry>localhost:4700</entry>
+              <entry>localhost:4700</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-port</entry>
+              <entry>port</entry>
+              <entry>548</entry>
+              <entry>548</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-signature</entry>
+              <entry>signature</entry>
+              <entry>auto</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fqdn</entry>
+              <entry>fqdn</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-unixcodepage</entry>
+              <entry>unix charset</entry>
+              <entry><emphasis role="bold">LOCALE</emphasis></entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-maccodepage</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-closevol</entry>
+              <entry>close vol</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-ntdomain</entry>
+              <entry>nt domain</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-ntseparator</entry>
+              <entry>nt separator</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-dircachesize</entry>
+              <entry>dircachesize</entry>
+              <entry>8192</entry>
+              <entry>8192</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-tcpsndbuf</entry>
+              <entry>tcpsndbuf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>OS default</entry>
+            </row>
+            <row>
+              <entry>-tcprcvbuf</entry>
+              <entry>tcprcvbuf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>OS default</entry>
+            </row>
+            <row>
+              <entry>-fcelistener</entry>
+              <entry>fce listener</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fcecoalesce</entry>
+              <entry>fce coalesce</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fceevents</entry>
+              <entry>fce events</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-fceholdfmod</entry>
+              <entry>fce holdfmod</entry>
+              <entry>60</entry>
+              <entry>60</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-mimicmodel</entry>
+              <entry>mimic model</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-adminauthuser</entry>
+              <entry>admin auth user</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-noacl2maccess</entry>
+              <entry>map acls</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>-[no]tcp</entry>
+              <entry>-</entry>
+              <entry>-tcp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>always TCP only</entry>
+            </row>
+            <row>
+              <entry>-[no]ddp</entry>
+              <entry>-</entry>
+              <entry>-noddp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]transall</entry>
+              <entry>-</entry>
+              <entry>-tcp -noddp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>always TCP only</entry>
+            </row>
+            <row>
+              <entry>-nodebug</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-[no]slp</entry>
+              <entry>-</entry>
+              <entry>-noslp</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>SLP support is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]uservolfirst</entry>
+              <entry>-</entry>
+              <entry>-nouservolfirst</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>uservol is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]uservol</entry>
+              <entry>-</entry>
+              <entry>-uservol</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>uservol is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-proxy</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-defaultvol</entry>
+              <entry>-</entry>
+              <entry>AppleVolumes.default</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>afp.conf only</entry>
+            </row>
+            <row>
+              <entry>-systemvol</entry>
+              <entry>-</entry>
+              <entry>AppleVolumes.system</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>afp.conf only</entry>
+            </row>
+            <row>
+              <entry>-loginmaxfail</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>not supported from the biginning</entry>
+            </row>
+            <row>
+              <entry>-unsetuplog</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-authprintdir</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-ddpaddr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>AppleTalk is obsoleted</entry>
+            </row>
+            <row>
+              <entry>-[no]icon</entry>
+              <entry>-</entry>
+              <entry>-noicon</entry>
+              <entry></entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>-keepsessions</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete. Use kill -HUP.</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from afp_ldap.conf to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old afp_ldap.conf</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Defalut Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>ldap_server</entry>
+              <entry>ldap server</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_method</entry>
+              <entry>ldap auth method</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_dn</entry>
+              <entry>ldap auth dn</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_auth_pw</entry>
+              <entry>ldap auth pw</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_userbase</entry>
+              <entry>ldap userbase</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_userscope</entry>
+              <entry>ldap userscope</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_groupbase</entry>
+              <entry>ldap groupbase</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_groupscope</entry>
+              <entry>ldap groupscope</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_uuid_attr</entry>
+              <entry>ldap uuid attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_uuid_string</entry>
+              <entry>ldap uuid string</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ldap_name_attr</entry>
+              <entry>ldap name attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry> ldap_group_attr</entry>
+              <entry>ldap group attr</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(G)</entry>
+              <entry>-</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table><table frame="all">
+        <title>from AppleVolumes.* to afp.conf</title>
+        <tgroup cols="6">
+          <colspec colname="c1" colnum="1" colwidth="1.0*"/>
+          <colspec colname="c2" colnum="2" colwidth="1.0*"/>
+          <colspec colname="c3" colnum="3" colwidth="1.0*"/>
+          <colspec colname="c4" colnum="4" colwidth="1.0*"/>
+          <colspec colname="c5" colnum="5" colwidth="1.0*"/>
+          <colspec colname="c6" colnum="6" colwidth="1.0*"/>
+          <thead>
+            <row>
+              <entry>Old AppleVolumes.*</entry>
+              <entry>New afp.conf</entry>
+              <entry>Old Default Value</entry>
+              <entry>New Defalut Value</entry>
+              <entry>Section</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>(leading-dot lines)</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>move to extmap.conf</entry>
+            </row>
+            <row>
+              <entry>:DEFAULT:</entry>
+              <entry>-</entry>
+              <entry>options:upriv,usedots</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use "vol preset ="</entry>
+            </row>
+            <row>
+              <entry>1st field ("~")</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use [Homes] section</entry>
+            </row>
+            <row>
+              <entry>1st field ("/path")</entry>
+              <entry>path</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>2nd field</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>use section name</entry>
+            </row>
+            <row>
+              <entry>allow:</entry>
+              <entry>valid users</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>deny:</entry>
+              <entry>invalid users</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>rwlist:</entry>
+              <entry>rwlist</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>rolist:</entry>
+              <entry>rolist</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>volcharset:</entry>
+              <entry>vol charset</entry>
+              <entry><emphasis role="bold">UTF8</emphasis></entry>
+              <entry><emphasis role="bold">(same as unix charset)</emphasis></entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>maccharset:</entry>
+              <entry>mac charset</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>MAC_ROMAN</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>veto:</entry>
+              <entry>veto files</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>cnidscheme:</entry>
+              <entry>cnid scheme</entry>
+              <entry>dbd</entry>
+              <entry>dbd</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>casefold:</entry>
+              <entry>casefold</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>adouble:</entry>
+              <entry>appledouble</entry>
+              <entry><emphasis role="bold">v2</emphasis></entry>
+              <entry><emphasis role="bold">ea</emphasis></entry>
+              <entry>(V)</entry>
+              <entry>v1, osx and sfm are obsoleted</entry>
+            </row>
+            <row>
+              <entry>cnidserver:</entry>
+              <entry>cnid server</entry>
+              <entry>localhost:4700</entry>
+              <entry>localhost:4700</entry>
+              <entry>(G)/(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>dbpath:</entry>
+              <entry>vol dbpath</entry>
+              <entry><emphasis role="bold">(volume directory)</emphasis></entry>
+              <entry><emphasis role="bold">var/netatalk/CNID/</emphasis></entry>
+              <entry>(G)</entry>
+              <entry>moved to $localstatedir</entry>
+            </row>
+            <row>
+              <entry>umask:</entry>
+              <entry>umask</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>dperm:</entry>
+              <entry>directory perm</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>fperm:</entry>
+              <entry>file perm</entry>
+              <entry>0000</entry>
+              <entry>0000</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>password:</entry>
+              <entry>password</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>root_preexec:</entry>
+              <entry>root preexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>preexec:</entry>
+              <entry>preexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>root_postexec:</entry>
+              <entry>root postexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>postexec:</entry>
+              <entry>postexec</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>allowed_hosts:</entry>
+              <entry>hosts allow</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>denied_hosts:</entry>
+              <entry>hosts deny</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>ea:</entry>
+              <entry>ea</entry>
+              <entry>auto</entry>
+              <entry>auto</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>volsizelimit:</entry>
+              <entry>vol size limit</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>perm:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>Use "directory perm" and "file perm"</entry>
+            </row>
+            <row>
+              <entry>forceuid:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>forcegid:</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:ro</entry>
+              <entry>read only</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:invisibledots</entry>
+              <entry>invisible dots</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nostat</entry>
+              <entry>stat vol</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:preexec_close</entry>
+              <entry>preexec close</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:root_preexec_close</entry>
+              <entry>root preexec close</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:upriv</entry>
+              <entry>unix priv</entry>
+              <entry>-</entry>
+              <entry><emphasis role="bold">yes</emphasis></entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nodev</entry>
+              <entry>cnid dev</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:illegalseq</entry>
+              <entry>illegal seq</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:tm</entry>
+              <entry>time machine</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:searchdb</entry>
+              <entry>search db</entry>
+              <entry>-</entry>
+              <entry>no</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nonetids</entry>
+              <entry>network ids</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:noacls</entry>
+              <entry>acls</entry>
+              <entry>-</entry>
+              <entry>yes</entry>
+              <entry>(V)</entry>
+              <entry>-</entry>
+            </row>
+            <row>
+              <entry>options:nohex</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>auto-convert from ":2f" to ":"</entry>
+            </row>
+            <row>
+              <entry>options:usedots</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>auto-convert from ":2e" to "."</entry>
+            </row>
+            <row>
+              <entry>options:nofileid</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:prodos</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:mswindows</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:crlf</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:noadouble</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:limitsize</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:dropbox</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:dropkludge</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:nocnidcache</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+            <row>
+              <entry>options:caseinsensitive</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>-</entry>
+              <entry>obsolete</entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table></para>
+  </sect1>
+
+  <sect1>
+    <title>To Do</title>
+
+    <para><itemizedlist>
+        <listitem>
+          <para>test <command>ad</command> utils with <option>appledouble =
+          ea</option></para>
+        </listitem>
+      </itemizedlist></para>
+  </sect1>
+</chapter>
diff --git a/doc/netatalk.html b/doc/netatalk.html
new file mode 100644 (file)
index 0000000..b0b7904
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+    <div id="header">
+        <div id="logo"></div>
+        <div id="menlinks">
+          <a href="/" title="Return to Netatalk home">[main]</a>
+          <a href="http://netatalk.sourceforge.net/wiki/" title="Netatalk Wiki">[wiki]</a>
+          <a href="/3.0/htmldocs" title="Netatalk Manual">[documentation]</a>
+          <a href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Download Netatalk from sourceforge">[downloads]</a>
+          <a href="/support.php" title="Support">[support]</a>
+          <a href="/links.php" title="Netatalk related links">[links]</a>
+          <img src="/gfx/end.gif" alt="" width="125" height="7" />
+        </div>
+    </div>
+</html>
diff --git a/doc/www/ReleaseNotes b/doc/www/ReleaseNotes
new file mode 100644 (file)
index 0000000..ab0441a
--- /dev/null
@@ -0,0 +1,294 @@
+Netatalk 3.0.3
+==============
+
+The Netatalk development team is proud to announce version 3.0.3 of
+the Netatalk File Sharing suite. This is the latest update to the 3.0
+release series. All users are encouraged to upgrade their systems to 3.0.3.
+
+Netatalk is a freely-available Open Source AFP fileserver.
+A *NIX/*BSD system running Netatalk is capable of serving many Macintosh
+clients simultaneously as an AppleShare file server (AFP).
+
+The suite contains:
+
+* netatalk   - the main server service controller
+* afpd       - the AFP file server daemin
+* cnid_metad - the CNID database multiplexing daemon
+* cnid_dbd   - the CNID database daemon serving CNIDs for AFP volumes
+* various supporting programs and utilities
+
+Summary of major new features and enhancements in 3.0
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* New ini style configuration file afp.conf which replaces all previous
+  configuration files
+* New default AppleDouble backend using filesystem Extended Attributes,
+  conversion from AppleDouble v2 is done automatically on access
+* New service controller process "netatalk" responsible for starting and
+  restarting afpd and cnid_metad as necessary
+* AppleTalk support has been removed
+* Coherent cross-platform locking with Solaris CIFS server
+
+Please make sure to read the upgrading section in the Netatalk online
+manual before trying to upgrade your system to 3.0!
+
+  http://netatalk.sourceforge.net/3.0/htmldocs/upgrade.html
+
+License
+~~~~~~~
+
+Netatalk is a Free/Open Source Software project and is released under
+the GNU General Public License (GPLv2).  The full license text is available
+at:
+
+  http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+Changes in 3.0.3
+~~~~~~~~~~~~~~~~
+* UPD: afpd: Increase default DSI server quantum to 1 MB
+* UPD: bundled libevent2 is now static
+* NEW: --with-lockfile=PATH configure option for specifying an
+       alternative path for the netatalk lockfile.
+* UPD: systemd service file use PIDFile and ExecReload.
+       From FR #70.
+* UPD: RedHat sysvinit: rm graceful, reimplement reload, add condrestart
+* FIX: Couldn't create folders on FreeBSD 9.1 ZFS fileystems.
+       Fixed bug #491.
+* FIX: Fix an issue with user homes when user home directory has not the
+       same name as the username.
+       Fixes bug #497.
+* UPD: Fix PAM config install, new default installation dir is
+       $sysconfdir/pam.d/. Add configure option --with-pam-confdir
+       to specify alternative path.
+* NEW: AFP stats about active session via dbus IPC. Client side python
+       program `afpstats`. Requires dbus, dbus-glib any python-dbus.
+       configure option --dbus-sysconf-dir for specifying dbus
+       system security configuration files.
+       New option 'afpstats' (default: no) which determines whether
+       to enable the feature or not.
+* NEW: configure option --with-init-dir
+* NEW: dtrace probes, cf include/atalk/afp_dtrace.d for available
+       probes.
+* UPD: Reload groups when reloading volumes. FR #71.
+* FIX: Attempt to read read-only ._ rfork results in disconnect.
+       Fixes bug #502.
+* FIX: File's ressource fork can't be read if metadata EA is missing.
+       Fixes bug #501.
+* FIX: Conversion from adouble v2 to ea for directories.
+       Fixes bug #500.
+* FIX: Error messages when mounting read-only filesystems.
+       Fixes bug #504.
+* FIX: Permissions of ._ AppleDouble ressource fork after conversion
+       from v2 to ea.
+       Fixes bug #505.
+* UPD: Use FreeBSD sendfile() capability to send protocol header.
+       From FR #75.
+* UPD: Increase IO size when sendfile() is not used.
+       From FR #76.
+* FIX: Can't set Finder label on symlinked folder with "follow symlinks = yes".
+       Fixes bug #508.
+* FIX: Setting POSIX ACLs on Linux
+       Fixes bug #506.
+* FIX: "ad ls" segfault if requested object is not in an AFP volume.
+       Fixes bug #496.
+
+Changes in 3.0.2
+~~~~~~~~~~~~~~~~
+* NEW: afpd: Put file extension type/creator mapping back in which had
+       been removed in 3.0.
+* NEW: afpd: new option 'ad domain'. From FR #66.
+* FIX: volumes and home share with symlinks in the path
+* FIX: Copying packages to a Netatalk share could fail, bug #469
+* FIX: Reloading volumes from config file was broken.  Fixes bug #474.
+* FIX: Fix _device-info service type registered with dns-sd API
+* FIX: Fix pathname bug for FCE modified event.
+* FIX: Remove length limitation of options like "valid users".
+       Fixes bug #473.
+* FIX: Dont copy our metadata EA in copyfile(). Fixes bug #452.
+* FIX: Fix an error where catalog search gave incomplete results.
+       Fixes bug #479.
+* REM: Remove TimeMachine volume used size FCE event.
+* UPD: Add quoting support to '[in]valid users' option. Fixes bug #472.
+* FIX: Install working PAM config on Solaris 11. Fixes bug #481.
+* FIX: Fix a race condition between dbd and the cnid_dbd daemon
+       which could result in users being disconnected from volumes
+       when dbd was scanning their volumes. Fixes bug #477.
+* FIX: Netatalk didn't start when the last line of the config file
+       afp.conf wasn't terminated by a newline. Fixes bug #476.
+* NEW: Add a new volumes option 'follow symlinks'. The default setting is
+       false, symlinks are not followed on the server. This is the same
+       behaviour as OS X's AFP server.
+       Setting the option to true causes afpd to follow symlinks on the
+       server. symlinks may point outside of the AFP volume, currently
+       afpd doesn't do any checks for "wide symlinks".
+* FIX: Automatic AppleDouble conversion to EAs failing for directories.
+       Fixes bug #486.
+* FIX: dbd failed to convert appledouble files of symlinks.
+       Fixes bug #490.
+
+Changes in 3.0.1
+~~~~~~~~~~~~~~~~
+* NEW: afpd: Optional "ldap uuid encoding = string | ms-guid" parameter to
+       afp.conf, allowing for usage of the binary objectGUID fields from
+       Active Directory.
+* FIX: afpd: Fix a Solaris 10 SPARC sendfilev bug
+* FIX: afpd: Fix a crash on FreeBSD
+* FIX: afpd: Fixes open file handle refcounting bug which was reported as
+       being unable to play movies off a Netatalk AFP share.
+       Bug ID 3559783.
+* FIX: afpd: Fix a possible data corruption when reading from and writing
+       to the server simultaniously under load
+* FIX: Fix possible alignment violations due to bad casts
+* FIX: dbd: Fix logging
+* FIX: apple_dump: Extended Attributes AppleDouble support for *BSD
+* FIX: handling of '/' and ':' in volume name
+* UPD: Install relevant includes necessary for building programs with
+       installed headers and shared lib libatalk
+* UPD: libevent configure args to pick up installed version. Removed
+       configure arg --disable-libevent, added configure args
+       --with-libevent-header|lib.
+* UPD: gentoo initscript: merge from portage netatalk.init,v 1.1
+* REM: Remove --with-smbsharemodes configure option, it was an
+       empty stub not yet implemented
+
+Changes in 3.0
+~~~~~~~~~~~~~~
+
+* UPD: afpd: force read only mode if cnid scheme is last
+* REM: afpd: removed global option "icon"
+* FIX: CNID path for user homes
+
+Changes in 3.0 beta2
+~~~~~~~~~~~~~~~~~~~~
+
+* UPD: Solaris and friends: Replace initscript with SMF manifest
+* FIX: Solaris and friends: resource fork handling
+
+Changes in 3.0 beta1
+~~~~~~~~~~~~~~~~~~~~
+
+* UPD: afpd: Performance tuning of read/write AFP operations. New option
+       "afp read locks" (default: no) which disables that the server
+       applies UNIX byte range locks to regions of files in AFP read and
+       write calls.
+* UPD: apple_dump: Extended Attributes AppleDouble support.
+       (*BSD is not supported yet)
+
+Changes in 3.0 alpha3
+~~~~~~~~~~~~~~~~~~~~~
+
+* NEW: afpd: Per volume "login message", NetAFP bug ID #18
+* NEW: afpd: Cross-platform locking (share modes) on Solaris and derivates
+       with Solaris CIFS/SMB server. Uses new Solaris fcntl F_SHARE share
+       reservation locking primitives. Enabled by default, set global
+       "solaris share reservations" option to false to disable it.
+* NEW: ad: ad set subcommand for changing Mac metadata on the server
+* UPD: unix charset is UTF8 by default
+       vol charset is same value as unix charset by default
+* UPD: .AppleDesktop/ are stored in $localstatedir/netatalk/CNID
+       (default: /var/netatalk/CNID), databases found in AFP volumes are
+       automatically moved
+* FIX: afpd: Server info packet was malformed resulting in broken
+       server names being displayed on clients
+* FIX: afpd: Byte order detection. Fixes an error where Netatalk on
+       OpenIndiana returned wrong volume size information.
+
+Changes in 3.0 alpha2
+~~~~~~~~~~~~~~~~~~~~~
+
+* UPD: afpd: Store '.' as is and '/' as ':' on the server, don't
+       CAP hexencode as "2e" and "2f" respectively
+* UPD: afdp: Automatic name conversion, renaming files and directories
+       containing CAP sequences to their not enscaped forms
+* UPD: afpd: Correct handling of user homes and users without homes
+* UPD: afpd: Perform complete automatic adouble:v2 to adouble:ea conversion
+       as root. Previously only unlinking the adouble:v2 file was done as root
+* UPD: dbd: -C option removes CAP encoding
+* UPD: Add graceful option to RedHat init script
+* UPD: Add --disable-bundled-libevent configure options When set to yes,
+       we rely on a properly installed version on libevent CPPFLAGS and LDFLAGS
+       should be set properly to pick that up
+* UPD: Run ldconfig on Linux at the end of make install
+* FIX: afpd: ad cp on appledouble = ea volumes
+* FIX: dbd: ignore ._ appledouble files
+* REM: Volumes options "use dots" and "hex encoding"
+
+Changes in 3.0 alpha1
+~~~~~~~~~~~~~~~~~~~~~
+
+* NEW: Central configuration file afp.conf which replaces all previous files
+* NEW: netatalk: service controller starting and restarting afpd and cnid_metad
+       as necessary
+* NEW: afpd: Extended Attributes AppleDouble backend (default)
+* UPD: CNID databases are stored in $localstatedir/netatalk/CNID
+       (default: /var/netatalk/CNID), databases found in AFP volumes are
+       automatically moved
+* UPD: Start scripts and service manifests have been changed to only start
+       the new netatalk service controller process
+* UPD: afpd: UNIX privileges and use dots enabled by default
+* UPD: afpd: Support for arbitrary AFP volumes using variable expansion has been
+       removed
+* UPD: afpd: afp_voluuid.conf and afp_signature.conf location has been
+       changed to $localstatedir/netatalk/ (default: /var/netatalk/)
+* UPD: afpd: default server messages dir changed to $localstatedir/netatalk/msg/
+* UPD: dbd: new option -C for conversion from AppleDouble v2 to ea
+* REM: AppleTalk support has been removed
+* REM: afpd: SLP and AFP proxy support have been removed
+* REM: afpd: legacy file extension to type/creator mapping has been removed
+* REM: afpd: AppleDouble backends v1, osx and sfm have been removed
+
+
+Supported Platforms
+~~~~~~~~~~~~~~~~~~~
+
+As of Netatalk 3.0 the following operating systems are supported:
+
+ * FreeBSD
+ * Linux
+ * OpenBSD
+ * NetBSD
+ * Solaris and derivates
+
+Netatalk may compile and run on other operating systems as well, but
+it is not well-tested on those.  We welcome patches and suggestions
+for enhancing the portability of Netatalk as well as success and failure
+stories.  Please write to netatalk-devel@lists.sourceforge.net.
+
+Availability
+~~~~~~~~~~~~
+
+Netatalk tar-balls can be found at:
+
+http://sourceforge.net/project/showfiles.php?group_id=8642
+
+Netatalk is also available via anonymous git.  See the SourceForge project
+site for anonymous git instructions. 
+
+Contact
+~~~~~~~
+
+For more information about Netatalk, see its web page at:
+
+http://netatalk.sourceforge.net/
+
+The project is hosted at SourceForge.  The SourceForge project page is
+located at:
+
+http://sourceforge.net/projects/netatalk/
+
+The Netatalk development team can be reached via the mailing list
+netatalk-devel@lists.sourceforge.net.  For subscription information and
+archives see Netatalk's SourceForge project page.
+
+netatalk-admins@lists.sourceforge.net is a mailing list for Netatalk
+system administrators.  For subscription information and archives see
+the Netatalk web page.
+
+Acknowledgements
+~~~~~~~~~~~~~~~~
+
+We would like to thank all contributors to the Netatalk project for
+their commitment.  Without the many suggestions, bug and problem reports,
+patches, and reviews this project wouldn't be where it is.
+
+ - The Netatalk Development Team, January 2013
diff --git a/doc/www/asciidoc.conf b/doc/www/asciidoc.conf
new file mode 100644 (file)
index 0000000..85b3df4
--- /dev/null
@@ -0,0 +1,610 @@
+#
+# asciidoc.conf
+#
+# Asciidoc global configuration file.
+# Contains backend independent configuration settings that are applied to all
+# AsciiDoc documents.
+#
+
+[miscellaneous]
+tabsize=8
+textwidth=70
+newline=\r\n
+
+[attributes]
+backend-alias-html=xhtml11
+backend-alias-docbook=docbook45
+toclevels=2
+sectids=
+iconsdir=./images/icons
+encoding=UTF-8
+# Uncomment to use xhtml11 quirks mode CSS.
+#quirks=
+# Uncomment to use the Pygments source highlighter instead of GNU highlighter.
+#pygments=
+# Uncomment to use deprecated quote attributes.
+#deprecated-quotes=
+empty=
+# Attribute and AttributeList element patterns.
+attributeentry-pattern=^:(?P<attrname>\w[^.]*?)(\.(?P<attrname2>.*?))?:(\s+(?P<attrvalue>.*))?$
+attributelist-pattern=(?u)(^\[\[(?P<id>[\w_:][\w_:.-]*)(,(?P<reftext>.*?))?\]\]$)|(^\[(?P<attrlist>.*)\]$)
+# Substitution attributes for escaping AsciiDoc processing.
+amp=&
+lt=<
+gt=>
+brvbar=|
+nbsp=&#160;
+zwsp=&#8203;
+wj=&#8288;
+deg=&#176;
+backslash=\
+two-colons=::
+two-semicolons=;;
+# DEPRECATED: underscore attribute names.
+two_colons=::
+two_semicolons=;;
+# Left and right single and double quote characters.
+# See http://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks
+lsquo=&#8216;
+rsquo=&#8217;
+ldquo=&#8220;
+rdquo=&#8221;
+
+[titles]
+subs=specialcharacters,quotes,replacements,macros,attributes,replacements2
+# Double-line title pattern and underlines.
+sectiontitle=^(?P<title>.*?)$
+underlines="==","--","~~","^^","++"
+# Single-line title patterns.
+sect0=^= +(?P<title>[\S].*?)( +=)?$
+sect1=^== +(?P<title>[\S].*?)( +==)?$
+sect2=^=== +(?P<title>[\S].*?)( +===)?$
+sect3=^==== +(?P<title>[\S].*?)( +====)?$
+sect4=^===== +(?P<title>[\S].*?)( +=====)?$
+blocktitle=^\.(?P<title>([^.\s].*)|(\.[^.\s].*))$
+
+[specialcharacters]
+&=&amp;
+<=&lt;
+>=&gt;
+
+[quotes]
+# The order is important, quotes are processed in conf file order.
+**=#strong
+*=strong
+``|''=doublequoted
+'=emphasis
+`|'=singlequoted
+ifdef::no-inline-literal[]
+`=monospaced
+endif::no-inline-literal[]
+# +++ and $$ quoting is applied to the +++ and $$ inline passthrough
+# macros to allow quoted attributes to be used.
+# This trick only works with inline passthrough macros.
++++=#unquoted
+$$=#unquoted
+++=#monospaced
++=monospaced
+__=#emphasis
+_=emphasis
+\##=#unquoted
+\#=unquoted
+^=#superscript
+~=#subscript
+
+[specialwords]
+emphasizedwords=
+strongwords=
+monospacedwords=
+
+[replacements]
+# Replacements performed in order of configuration file entry.  The first entry
+# of each replacement pair performs the (non-escaped) replacement, the second
+# strips the backslash from the escaped replacement.
+
+# (C) Copyright (entity reference &copy;)
+(?<!\\)\(C\)=&#169;
+\\\(C\)=(C)
+
+# (R) registered trade mark (entity reference &reg;
+(?<!\\)\(R\)=&#174;
+\\\(R\)=(R)
+
+# (TM) Trademark (entity reference &trade;)
+(?<!\\)\(TM\)=&#8482;
+\\\(TM\)=(TM)
+
+# -- Spaced and unspaced em dashes (entity reference &mdash;).
+# Space on both sides is translated to thin space characters.
+(^-- )=&#8212;&#8201;
+(\n-- )|( -- )|( --\n)=&#8201;&#8212;&#8201;
+(\w)--(\w)=\1&#8212;\2
+\\--(?!-)=--
+
+# Replace vertical typewriter apostrophe with punctuation apostrophe.
+(\w)'(\w)=\1&#8217;\2
+(\w)\\'(\w)=\1'\2
+
+# ... Ellipsis (entity reference &hellip;)
+(?<!\\)\.\.\.=&#8230;
+\\\.\.\.=...
+
+# Arrows from the Arrows block of Unicode.
+# -> right arrow
+(?<!\\)-&gt;=&#8594;
+\\-&gt;=-&gt;
+# => right double arrow
+(?<!\\)\=&gt;=&#8658;
+\\\=&gt;==&gt;
+# <- left arrow
+(?<!\\)&lt;-=&#8592;
+\\&lt;-=&lt;-
+# <= left double arrow
+(?<!\\)&lt;\==&#8656;
+\\&lt;\==&lt;=
+
+# Arbitrary entity references.
+(?<!\\)&amp;([:_#a-zA-Z][:_.\-\w]*?;)=&\1
+\\(&amp;[:_#a-zA-Z][:_.\-\w]*?;)=\1
+
+#-----------
+# Paragraphs
+#-----------
+[paradef-default]
+delimiter=(?s)(?P<text>\S.*)
+posattrs=style
+style=normal
+template::[paragraph-styles]
+
+[paradef-literal]
+delimiter=(?s)(?P<text>\s+.*)
+options=listelement
+posattrs=style
+style=literal
+template::[paragraph-styles]
+
+[paradef-admonition]
+delimiter=(?s)^\s*(?P<style>NOTE|TIP|IMPORTANT|WARNING|CAUTION):\s+(?P<text>.+)
+template::[paragraph-styles]
+
+[paragraph-styles]
+normal-style=template="paragraph"
+verse-style=template="verseparagraph",posattrs=["style","attribution","citetitle"]
+quote-style=template="quoteparagraph",posattrs=["style","attribution","citetitle"]
+literal-style=template="literalparagraph",subs=["verbatim"]
+listing-style=template="listingparagraph",subs=["verbatim"]
+NOTE-style=template="admonitionparagraph",name="note",caption="{note-caption}"
+TIP-style=template="admonitionparagraph",name="tip",caption="{tip-caption}"
+IMPORTANT-style=template="admonitionparagraph",name="important",caption="{important-caption}"
+WARNING-style=template="admonitionparagraph",name="warning",caption="{warning-caption}"
+CAUTION-style=template="admonitionparagraph",name="caution",caption="{caution-caption}"
+
+[literalparagraph]
+template::[literalblock]
+
+[verseparagraph]
+template::[verseblock]
+
+[quoteparagraph]
+template::[quoteblock]
+
+[listingparagraph]
+template::[listingblock]
+
+[macros]
+#--------------
+# Inline macros
+#--------------
+# Backslash prefix required for escape processing.
+# (?s) re flag for line spanning.
+
+# Macros using default syntax.
+(?su)(?<!\w)[\\]?(?P<name>http|https|ftp|file|irc|mailto|callto|image|link|anchor|xref|indexterm):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+# These URL types don't require any special attribute list formatting.
+(?su)(?<!\S)[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])=
+# Allow a leading parenthesis and square bracket.
+(?su)(?<\=[([])[\\]?(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])=
+# Allow <> brackets.
+(?su)[\\]?&lt;(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])&gt;=
+
+# Email addresses don't require special attribute list formatting.
+# The before ">: and after "< character exclusions stop multiple substitution.
+(?su)(?<![">:\w._/-])[\\]?(?P<target>\w[\w._-]*@[\w._-]*\w)(?!["<\w_-])=mailto
+
+# Allow footnote macros hard up against the preceding word so the footnote mark
+# can be placed against the noted text without an intervening space
+# (http://groups.google.com/group/asciidoc/browse_frm/thread/e1dcb7ee0efc17b5).
+(?su)[\\]?(?P<name>footnote|footnoteref):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+# Anchor: [[[id]]]. Bibliographic anchor.
+(?su)[\\]?\[\[\[(?P<attrlist>[\w_:][\w_:.-]*?)\]\]\]=anchor3
+# Anchor: [[id,xreflabel]]
+(?su)[\\]?\[\[(?P<attrlist>[\w"_:].*?)\]\]=anchor2
+# Link: <<id,text>>
+(?su)[\\]?&lt;&lt;(?P<attrlist>[\w"_:].*?)&gt;&gt;=xref2
+
+ifdef::asciidoc7compatible[]
+# Index term: ++primary,secondary,tertiary++
+(?su)(?<!\S)[\\]?\+\+(?P<attrlist>[^+].*?)\+\+(?!\+)=indexterm
+# Index term: +primary+
+# Follows ++...++ macro otherwise it will match them.
+(?<!\S)[\\]?\+(?P<attrlist>[^\s\+][^+].*?)\+(?!\+)=indexterm2
+endif::asciidoc7compatible[]
+
+ifndef::asciidoc7compatible[]
+# Index term: (((primary,secondary,tertiary)))
+(?su)(?<!\()[\\]?\(\(\((?P<attrlist>[^(].*?)\)\)\)(?!\))=indexterm
+# Index term: ((primary))
+# Follows (((...))) macro otherwise it will match them.
+(?<!\()[\\]?\(\((?P<attrlist>[^\s\(][^(].*?)\)\)(?!\))=indexterm2
+endif::asciidoc7compatible[]
+
+# Callout
+[\\]?&lt;(?P<index>\d+)&gt;=callout
+
+# Passthrough macros.
+(?su)[\\]?(?P<name>pass):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[]
+
+# Triple-plus and double-dollar inline passthroughs.
+(?su)[\\]?\+\+\+(?P<passtext>.*?)\+\+\+=pass[]
+(?su)[\\]?\$\$(?P<passtext>.*?)\$\$=pass[specialcharacters]
+
+# Inline literal.
+ifndef::no-inline-literal[]
+(?su)(?<![`\w])([\\]?`(?P<passtext>[^`\s]|[^`\s].*?\S)`)(?![`\w])=literal[specialcharacters]
+endif::no-inline-literal[]
+
+# Inline comment.
+(?mu)^[\\]?//(?P<passtext>[^/].*|)$=comment[specialcharacters]
+
+# Default (catchall) inline macro is not implemented so there is no ambiguity
+# with previous definition that could result in double substitution of escaped
+# references.
+#(?su)[\\]?(?P<name>\w(\w|-)*?):(?P<target>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=
+
+#-------------
+# Block macros
+#-------------
+# Macros using default syntax.
+(?u)^(?P<name>image|unfloat)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
+
+# Passthrough macros.
+(?u)^(?P<name>pass)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#
+
+^'{3,}$=#ruler
+^<{3,}$=#pagebreak
+^//(?P<passtext>[^/].*|)$=#comment[specialcharacters]
+
+[unfloat-blockmacro]
+# Implemented in HTML backends.
+
+#-----------------
+# Delimited blocks
+#-----------------
+[blockdef-comment]
+delimiter=^/{4,}$
+options=skip
+
+[blockdef-sidebar]
+delimiter=^\*{4,}$
+template=sidebarblock
+options=sectionbody
+posattrs=style
+# DEPRECATED: Use Openblock instead.
+abstract-style=template="abstractblock"
+
+[blockdef-open]
+# A block without opening or closing tags.
+delimiter=^--$
+template=openblock
+options=sectionbody
+posattrs=style
+abstract-style=template="abstractblock"
+partintro-style=template="partintroblock"
+
+[blockdef-pass]
+delimiter=^\+{4,}$
+template=passblock
+# Default subs choosen for backward compatibility.
+subs=attributes,macros
+posattrs=style
+pass-style=template="passblock",subs=[]
+
+[blockdef-listing]
+delimiter=^-{4,}$
+template=listingblock
+subs=verbatim
+posattrs=style
+
+[blockdef-literal]
+delimiter=^\.{4,}$
+template=literalblock
+subs=verbatim
+posattrs=style
+listing-style=template="listingblock"
+# DEPRECATED: Use verse style on quote blocks instead.
+verse-style=template="verseblock",subs="normal"
+
+[blockdef-quote]
+delimiter=^_{4,}$
+subs=normal
+style=quote
+posattrs=style,attribution,citetitle
+quote-style=template="quoteblock",options=("sectionbody",)
+verse-style=template="verseblock"
+
+[blockdef-example]
+delimiter=^={4,}$
+template=exampleblock
+options=sectionbody
+posattrs=style
+NOTE-style=template="admonitionblock",name="note",caption="{note-caption}"
+TIP-style=template="admonitionblock",name="tip",caption="{tip-caption}"
+IMPORTANT-style=template="admonitionblock",name="important",caption="{important-caption}"
+WARNING-style=template="admonitionblock",name="warning",caption="{warning-caption}"
+CAUTION-style=template="admonitionblock",name="caution",caption="{caution-caption}"
+
+# For use by custom filters.
+# DEPRECATED: No longer used, a styled listing block (blockdef-listing) is preferable.
+[blockdef-filter]
+delimiter=^~{4,}$
+template=listingblock
+subs=none
+posattrs=style
+
+#-------
+# Lists
+#-------
+[listdef-bulleted]
+# - bullets.
+delimiter=^\s*- +(?P<text>.+)$
+posattrs=style
+type=bulleted
+tags=bulleted
+callout-style=tags="callout"
+bibliography-style=tags="bibliography"
+
+[listdef-bulleted1]
+# * bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\* +(?P<text>.+)$
+
+[listdef-bulleted2]
+# ** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{2} +(?P<text>.+)$
+
+[listdef-bulleted3]
+# *** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{3} +(?P<text>.+)$
+
+[listdef-bulleted4]
+# **** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{4} +(?P<text>.+)$
+
+[listdef-bulleted5]
+# ***** bullets.
+template::[listdef-bulleted]
+delimiter=^\s*\*{5} +(?P<text>.+)$
+
+[listdef-arabic]
+# Arabic numbering.
+delimiter=^\s*(?P<index>\d+\.) +(?P<text>.+)$
+posattrs=style
+type=numbered
+tags=numbered
+style=arabic
+
+[listdef-loweralpha]
+# Lower alpha numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[a-z]\.) +(?P<text>.+)$
+style=loweralpha
+
+[listdef-upperalpha]
+# Upper alpha numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[A-Z]\.) +(?P<text>.+)$
+style=upperalpha
+
+[listdef-lowerroman]
+# Lower roman numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[ivx]+\)) +(?P<text>.+)$
+style=lowerroman
+
+[listdef-upperroman]
+# Upper roman numbering.
+template::[listdef-arabic]
+delimiter=^\s*(?P<index>[IVX]+\)) +(?P<text>.+)$
+style=upperroman
+
+[listdef-numbered1]
+# . numbering.
+template::[listdef-arabic]
+delimiter=^\s*\. +(?P<text>.+)$
+
+[listdef-numbered2]
+# .. numbering.
+template::[listdef-loweralpha]
+delimiter=^\s*\.{2} +(?P<text>.+)$
+
+[listdef-numbered3]
+# ... numbering.
+template::[listdef-lowerroman]
+delimiter=^\s*\.{3} +(?P<text>.+)$
+
+[listdef-numbered4]
+# .... numbering.
+template::[listdef-upperalpha]
+delimiter=^\s*\.{4} +(?P<text>.+)$
+
+[listdef-numbered5]
+# ..... numbering.
+template::[listdef-upperroman]
+delimiter=^\s*\.{5} +(?P<text>.+)$
+
+[listdef-labeled]
+# label:: item.
+delimiter=^\s*(?P<label>.*[^:])::(\s+(?P<text>.+))?$
+posattrs=style
+type=labeled
+tags=labeled
+vertical-style=tags="labeled"
+horizontal-style=tags="horizontal"
+glossary-style=tags="glossary"
+qanda-style=tags="qanda"
+
+[listdef-labeled2]
+# label;; item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^;]);;(\s+(?P<text>.+))?$
+
+[listdef-labeled3]
+# label::: item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^:]):{3}(\s+(?P<text>.+))?$
+
+[listdef-labeled4]
+# label:::: item.
+template::[listdef-labeled]
+delimiter=^\s*(?P<label>.*[^:]):{4}(\s+(?P<text>.+))?$
+
+[listdef-callout]
+posattrs=style
+delimiter=^<?(?P<index>\d*>) +(?P<text>.+)$
+type=callout
+tags=callout
+style=arabic
+
+# DEPRECATED: Old list syntax.
+[listdef-qanda]
+posattrs=style
+delimiter=^\s*(?P<label>.*\S)\?\?$
+type=labeled
+tags=qanda
+
+# DEPRECATED: Old list syntax.
+[listdef-bibliography]
+posattrs=style
+delimiter=^\+ +(?P<text>.+)$
+type=bulleted
+tags=bibliography
+
+# DEPRECATED: Old list syntax.
+[listdef-glossary]
+delimiter=^(?P<label>.*\S):-$
+posattrs=style
+type=labeled
+tags=glossary
+
+#-------
+# Tables
+#-------
+[tabledef-default]
+delimiter=^\|={3,}$
+posattrs=style
+template=table
+default-style=tags="default"
+verse-style=tags="verse"
+literal-style=tags="literal",subs=["specialcharacters"]
+emphasis-style=tags="emphasis"
+strong-style=tags="strong"
+monospaced-style=tags="monospaced"
+header-style=tags="header"
+asciidoc-style=tags="asciidoc",subs=[],filter='python "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"}{icons? -a icons -a "iconsdir={iconsdir}"}{imagesdir? -a "imagesdir={imagesdir}"}{data-uri? -a data-uri} -a "indir={indir}"{trace? -a "trace={trace}"} -s -'
+
+[tabledef-nested]
+# Same as [tabledef-default] but with different delimiter and separator.
+delimiter=^!={3,}$
+separator=((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<style>[a-z])?)?!
+posattrs=style
+template=table
+verse-style=tags="verse"
+literal-style=tags="literal",subs=["specialcharacters"]
+emphasis-style=tags="emphasis"
+strong-style=tags="strong"
+monospaced-style=tags="monospaced"
+header-style=tags="header"
+asciidoc-style=tags="asciidoc",subs=[],filter='python "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"} -s -'
+
+#----------------------------------------
+# Common block and macro markup templates
+#----------------------------------------
+[comment-inlinemacro]
+# Outputs nothing.
+
+[comment-blockmacro]
+# Outputs nothing.
+
+[pass-blockmacro]
+{passtext}
+
+[pass-inlinemacro]
+template::[pass-blockmacro]
+
+[passblock]
+|
+
+[filter-image-blockmacro]
+# Synthesize missing target attribute for filter generated file names.
+# The tag split | ensures missing target file names are auto-generated
+# before the filter is executed, the remainder (the [image-blockmacro])
+# is excuted after the filter to ensure data URI encoding comes after
+# the image is created.
+{target%}{counter2:target-number}
+{target%}{set2:target:{docname}__{target-number}.png}
+|
+template::[image-blockmacro]
+
+#----------------------------------
+# Default special section templates
+#----------------------------------
+[abstract]
+template::[sect1]
+
+[colophon]
+template::[sect1]
+
+[dedication]
+template::[sect1]
+
+[preface]
+template::[sect1]
+
+[appendix]
+template::[sect1]
+
+[glossary]
+template::[sect1]
+
+[bibliography]
+template::[sect1]
+
+[index]
+template::[sect1]
+
+[synopsis]
+template::[sect1]
+
+#--------------------------------------------------------------------
+# Deprecated old table definitions.
+#
+
+[old_tabledef-default]
+fillchar=-
+format=fixed
+
+[old_tabledef-csv]
+fillchar=~
+format=csv
+
+[old_tabledef-dsv]
+fillchar=_
+format=dsv
+
+# End of deprecated old table definitions.
+#--------------------------------------------------------------------
diff --git a/doc/www/asciidoc.py b/doc/www/asciidoc.py
new file mode 100755 (executable)
index 0000000..7846de3
--- /dev/null
@@ -0,0 +1,5902 @@
+#!/usr/bin/env python
+"""
+asciidoc - converts an AsciiDoc text file to HTML or DocBook
+
+Copyright (C) 2002-2010 Stuart Rackham. Free use of this software is granted
+under the terms of the GNU General Public License (GPL).
+"""
+
+import sys, os, re, time, traceback, tempfile, subprocess, codecs, locale, unicodedata
+
+### Used by asciidocapi.py ###
+VERSION = '8.6.5'           # See CHANGLOG file for version history.
+
+MIN_PYTHON_VERSION = 2.4    # Require this version of Python or better.
+
+#---------------------------------------------------------------------------
+# Program constants.
+#---------------------------------------------------------------------------
+DEFAULT_BACKEND = 'html'
+DEFAULT_DOCTYPE = 'article'
+# Allowed substitution options for List, Paragraph and DelimitedBlock
+# definition subs entry.
+SUBS_OPTIONS = ('specialcharacters','quotes','specialwords',
+    'replacements', 'attributes','macros','callouts','normal','verbatim',
+    'none','replacements2')
+# Default value for unspecified subs and presubs configuration file entries.
+SUBS_NORMAL = ('specialcharacters','quotes','attributes',
+    'specialwords','replacements','macros','replacements2')
+SUBS_VERBATIM = ('specialcharacters','callouts')
+
+NAME_RE = r'(?u)[^\W\d][-\w]*'  # Valid section or attribute name.
+OR, AND = ',', '+'              # Attribute list separators.
+
+
+#---------------------------------------------------------------------------
+# Utility functions and classes.
+#---------------------------------------------------------------------------
+
+class EAsciiDoc(Exception): pass
+
+class OrderedDict(dict):
+    """
+    Dictionary ordered by insertion order.
+    Python Cookbook: Ordered Dictionary, Submitter: David Benjamin.
+    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+    """
+    def __init__(self, d=None, **kwargs):
+        self._keys = []
+        if d is None: d = kwargs
+        dict.__init__(self, d)
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+    def __setitem__(self, key, item):
+        dict.__setitem__(self, key, item)
+        if key not in self._keys: self._keys.append(key)
+    def clear(self):
+        dict.clear(self)
+        self._keys = []
+    def copy(self):
+        d = dict.copy(self)
+        d._keys = self._keys[:]
+        return d
+    def items(self):
+        return zip(self._keys, self.values())
+    def keys(self):
+        return self._keys
+    def popitem(self):
+        try:
+            key = self._keys[-1]
+        except IndexError:
+            raise KeyError('dictionary is empty')
+        val = self[key]
+        del self[key]
+        return (key, val)
+    def setdefault(self, key, failobj = None):
+        dict.setdefault(self, key, failobj)
+        if key not in self._keys: self._keys.append(key)
+    def update(self, d=None, **kwargs):
+        if d is None:
+            d = kwargs
+        dict.update(self, d)
+        for key in d.keys():
+            if key not in self._keys: self._keys.append(key)
+    def values(self):
+        return map(self.get, self._keys)
+
+class AttrDict(dict):
+    """
+    Like a dictionary except values can be accessed as attributes i.e. obj.foo
+    can be used in addition to obj['foo'].
+    If an item is not present None is returned.
+    """
+    def __getattr__(self, key):
+        try: return self[key]
+        except KeyError: return None
+    def __setattr__(self, key, value):
+        self[key] = value
+    def __delattr__(self, key):
+        try: del self[key]
+        except KeyError, k: raise AttributeError, k
+    def __repr__(self):
+        return '<AttrDict ' + dict.__repr__(self) + '>'
+    def __getstate__(self):
+        return dict(self)
+    def __setstate__(self,value):
+        for k,v in value.items(): self[k]=v
+
+class InsensitiveDict(dict):
+    """
+    Like a dictionary except key access is case insensitive.
+    Keys are stored in lower case.
+    """
+    def __getitem__(self, key):
+        return dict.__getitem__(self, key.lower())
+    def __setitem__(self, key, value):
+        dict.__setitem__(self, key.lower(), value)
+    def has_key(self, key):
+        return dict.has_key(self,key.lower())
+    def get(self, key, default=None):
+        return dict.get(self, key.lower(), default)
+    def update(self, dict):
+        for k,v in dict.items():
+            self[k] = v
+    def setdefault(self, key, default = None):
+        return dict.setdefault(self, key.lower(), default)
+
+
+class Trace(object):
+    """
+    Used in conjunction with the 'trace' attribute to generate diagnostic
+    output. There is a single global instance of this class named trace.
+    """
+    SUBS_NAMES = ('specialcharacters','quotes','specialwords',
+                  'replacements', 'attributes','macros','callouts',
+                  'replacements2')
+    def __init__(self):
+        self.name_re = ''        # Regexp pattern to match trace names.
+        self.linenos = True
+        self.offset = 0
+    def __call__(self, name, before, after=None):
+        """
+        Print trace message if tracing is on and the trace 'name' matches the
+        document 'trace' attribute (treated as a regexp).
+        'before' is the source text before substitution; 'after' text is the
+        source text after substitutuion.
+        The 'before' and 'after' messages are only printed if they differ.
+        """
+        name_re = document.attributes.get('trace')
+        if name_re == 'subs':    # Alias for all the inline substitutions.
+            name_re = '|'.join(self.SUBS_NAMES)
+        self.name_re = name_re
+        if self.name_re is not None:
+            msg = message.format(name, 'TRACE: ', self.linenos, offset=self.offset)
+            if before != after and re.match(self.name_re,name):
+                if is_array(before):
+                    before = '\n'.join(before)
+                if after is None:
+                    msg += '\n%s\n' % before
+                else:
+                    if is_array(after):
+                        after = '\n'.join(after)
+                    msg += '\n<<<\n%s\n>>>\n%s\n' % (before,after)
+                message.stderr(msg)
+
+class Message:
+    """
+    Message functions.
+    """
+    PROG = os.path.basename(os.path.splitext(__file__)[0])
+
+    def __init__(self):
+        # Set to True or False to globally override line numbers method
+        # argument. Has no effect when set to None.
+        self.linenos = None
+        self.messages = []
+
+    def stdout(self,msg):
+        print msg
+
+    def stderr(self,msg=''):
+        self.messages.append(msg)
+        if __name__ == '__main__':
+            sys.stderr.write('%s: %s%s' % (self.PROG, msg, os.linesep))
+
+    def verbose(self, msg,linenos=True):
+        if config.verbose:
+            msg = self.format(msg,linenos=linenos)
+            self.stderr(msg)
+
+    def warning(self, msg,linenos=True,offset=0):
+        msg = self.format(msg,'WARNING: ',linenos,offset=offset)
+        document.has_warnings = True
+        self.stderr(msg)
+
+    def deprecated(self, msg, linenos=True):
+        msg = self.format(msg, 'DEPRECATED: ', linenos)
+        self.stderr(msg)
+
+    def format(self, msg, prefix='', linenos=True, cursor=None, offset=0):
+        """Return formatted message string."""
+        if self.linenos is not False and ((linenos or self.linenos) and reader.cursor):
+            if cursor is None:
+                cursor = reader.cursor
+            prefix += '%s: line %d: ' % (os.path.basename(cursor[0]),cursor[1]+offset)
+        return prefix + msg
+
+    def error(self, msg, cursor=None, halt=False):
+        """
+        Report fatal error.
+        If halt=True raise EAsciiDoc exception.
+        If halt=False don't exit application, continue in the hope of reporting
+        all fatal errors finishing with a non-zero exit code.
+        """
+        if halt:
+            raise EAsciiDoc, self.format(msg,linenos=False,cursor=cursor)
+        else:
+            msg = self.format(msg,'ERROR: ',cursor=cursor)
+            self.stderr(msg)
+            document.has_errors = True
+
+    def unsafe(self, msg):
+        self.error('unsafe: '+msg)
+
+
+def userdir():
+    """
+    Return user's home directory or None if it is not defined.
+    """
+    result = os.path.expanduser('~')
+    if result == '~':
+        result = None
+    return result
+
+def localapp():
+    """
+    Return True if we are not executing the system wide version
+    i.e. the configuration is in the executable's directory.
+    """
+    return os.path.isfile(os.path.join(APP_DIR, 'asciidoc.conf'))
+
+def file_in(fname, directory):
+    """Return True if file fname resides inside directory."""
+    assert os.path.isfile(fname)
+    # Empty directory (not to be confused with None) is the current directory.
+    if directory == '':
+        directory = os.getcwd()
+    else:
+        assert os.path.isdir(directory)
+        directory = os.path.realpath(directory)
+    fname = os.path.realpath(fname)
+    return os.path.commonprefix((directory, fname)) == directory
+
+def safe():
+    return document.safe
+
+def is_safe_file(fname, directory=None):
+    # A safe file must reside in directory directory (defaults to the source
+    # file directory).
+    if directory is None:
+        if document.infile == '<stdin>':
+           return not safe()
+        directory = os.path.dirname(document.infile)
+    elif directory == '':
+        directory = '.'
+    return (
+        not safe()
+        or file_in(fname, directory)
+        or file_in(fname, APP_DIR)
+        or file_in(fname, CONF_DIR)
+    )
+
+def safe_filename(fname, parentdir):
+    """
+    Return file name which must reside in the parent file directory.
+    Return None if file is not found or not safe.
+    """
+    if not os.path.isabs(fname):
+        # Include files are relative to parent document
+        # directory.
+        fname = os.path.normpath(os.path.join(parentdir,fname))
+    if not os.path.isfile(fname):
+        message.warning('include file not found: %s' % fname)
+        return None
+    if not is_safe_file(fname, parentdir):
+        message.unsafe('include file: %s' % fname)
+        return None
+    return fname
+
+def assign(dst,src):
+    """Assign all attributes from 'src' object to 'dst' object."""
+    for a,v in src.__dict__.items():
+        setattr(dst,a,v)
+
+def strip_quotes(s):
+    """Trim white space and, if necessary, quote characters from s."""
+    s = s.strip()
+    # Strip quotation mark characters from quoted strings.
+    if len(s) >= 3 and s[0] == '"' and s[-1] == '"':
+        s = s[1:-1]
+    return s
+
+def is_re(s):
+    """Return True if s is a valid regular expression else return False."""
+    try: re.compile(s)
+    except: return False
+    else: return True
+
+def re_join(relist):
+    """Join list of regular expressions re1,re2,... to single regular
+    expression (re1)|(re2)|..."""
+    if len(relist) == 0:
+        return None
+    result = []
+    # Delete named groups to avoid ambiguity.
+    for s in relist:
+        result.append(re.sub(r'\?P<\S+?>','',s))
+    result = ')|('.join(result)
+    result = '('+result+')'
+    return result
+
+def validate(value,rule,errmsg):
+    """Validate value against rule expression. Throw EAsciiDoc exception with
+    errmsg if validation fails."""
+    try:
+        if not eval(rule.replace('$',str(value))):
+            raise EAsciiDoc,errmsg
+    except Exception:
+        raise EAsciiDoc,errmsg
+    return value
+
+def lstrip_list(s):
+    """
+    Return list with empty items from start of list removed.
+    """
+    for i in range(len(s)):
+        if s[i]: break
+    else:
+        return []
+    return s[i:]
+
+def rstrip_list(s):
+    """
+    Return list with empty items from end of list removed.
+    """
+    for i in range(len(s)-1,-1,-1):
+        if s[i]: break
+    else:
+        return []
+    return s[:i+1]
+
+def strip_list(s):
+    """
+    Return list with empty items from start and end of list removed.
+    """
+    s = lstrip_list(s)
+    s = rstrip_list(s)
+    return s
+
+def is_array(obj):
+    """
+    Return True if object is list or tuple type.
+    """
+    return isinstance(obj,list) or isinstance(obj,tuple)
+
+def dovetail(lines1, lines2):
+    """
+    Append list or tuple of strings 'lines2' to list 'lines1'.  Join the last
+    non-blank item in 'lines1' with the first non-blank item in 'lines2' into a
+    single string.
+    """
+    assert is_array(lines1)
+    assert is_array(lines2)
+    lines1 = strip_list(lines1)
+    lines2 = strip_list(lines2)
+    if not lines1 or not lines2:
+        return list(lines1) + list(lines2)
+    result = list(lines1[:-1])
+    result.append(lines1[-1] + lines2[0])
+    result += list(lines2[1:])
+    return result
+
+def dovetail_tags(stag,content,etag):
+    """Merge the end tag with the first content line and the last
+    content line with the end tag. This ensures verbatim elements don't
+    include extraneous opening and closing line breaks."""
+    return dovetail(dovetail(stag,content), etag)
+
+def parse_attributes(attrs,dict):
+    """Update a dictionary with name/value attributes from the attrs string.
+    The attrs string is a comma separated list of values and keyword name=value
+    pairs. Values must preceed keywords and are named '1','2'... The entire
+    attributes list is named '0'. If keywords are specified string values must
+    be quoted. Examples:
+
+    attrs: ''
+    dict: {}
+
+    attrs: 'hello,world'
+    dict: {'2': 'world', '0': 'hello,world', '1': 'hello'}
+
+    attrs: '"hello", planet="earth"'
+    dict: {'planet': 'earth', '0': '"hello",planet="earth"', '1': 'hello'}
+    """
+    def f(*args,**keywords):
+        # Name and add aguments '1','2'... to keywords.
+        for i in range(len(args)):
+            if not str(i+1) in keywords:
+                keywords[str(i+1)] = args[i]
+        return keywords
+
+    if not attrs:
+        return
+    dict['0'] = attrs
+    # Replace line separators with spaces so line spanning works.
+    s = re.sub(r'\s', ' ', attrs)
+    try:
+        d = eval('f('+s+')')
+        # Attributes must evaluate to strings, numbers or None.
+        for v in d.values():
+            if not (isinstance(v,str) or isinstance(v,int) or isinstance(v,float) or v is None):
+                raise Exception
+    except Exception:
+        s = s.replace('"','\\"')
+        s = s.split(',')
+        s = map(lambda x: '"' + x.strip() + '"', s)
+        s = ','.join(s)
+        try:
+            d = eval('f('+s+')')
+        except Exception:
+            return  # If there's a syntax error leave with {0}=attrs.
+        for k in d.keys():  # Drop any empty positional arguments.
+            if d[k] == '': del d[k]
+    dict.update(d)
+    assert len(d) > 0
+
+def parse_named_attributes(s,attrs):
+    """Update a attrs dictionary with name="value" attributes from the s string.
+    Returns False if invalid syntax.
+    Example:
+    attrs: 'star="sun",planet="earth"'
+    dict: {'planet':'earth', 'star':'sun'}
+    """
+    def f(**keywords): return keywords
+
+    try:
+        d = eval('f('+s+')')
+        attrs.update(d)
+        return True
+    except Exception:
+        return False
+
+def parse_list(s):
+    """Parse comma separated string of Python literals. Return a tuple of of
+    parsed values."""
+    try:
+        result = eval('tuple(['+s+'])')
+    except Exception:
+        raise EAsciiDoc,'malformed list: '+s
+    return result
+
+def parse_options(options,allowed,errmsg):
+    """Parse comma separated string of unquoted option names and return as a
+    tuple of valid options. 'allowed' is a list of allowed option values.
+    If allowed=() then all legitimate names are allowed.
+    'errmsg' is an error message prefix if an illegal option error is thrown."""
+    result = []
+    if options:
+        for s in re.split(r'\s*,\s*',options):
+            if (allowed and s not in allowed) or not is_name(s):
+                raise EAsciiDoc,'%s: %s' % (errmsg,s)
+            result.append(s)
+    return tuple(result)
+
+def symbolize(s):
+    """Drop non-symbol characters and convert to lowercase."""
+    return re.sub(r'(?u)[^\w\-_]', '', s).lower()
+
+def is_name(s):
+    """Return True if s is valid attribute, macro or tag name
+    (starts with alpha containing alphanumeric and dashes only)."""
+    return re.match(r'^'+NAME_RE+r'$',s) is not None
+
+def subs_quotes(text):
+    """Quoted text is marked up and the resulting text is
+    returned."""
+    keys = config.quotes.keys()
+    for q in keys:
+        i = q.find('|')
+        if i != -1 and q != '|' and q != '||':
+            lq = q[:i]      # Left quote.
+            rq = q[i+1:]    # Right quote.
+        else:
+            lq = rq = q
+        tag = config.quotes[q]
+        if not tag: continue
+        # Unconstrained quotes prefix the tag name with a hash.
+        if tag[0] == '#':
+            tag = tag[1:]
+            # Unconstrained quotes can appear anywhere.
+            reo = re.compile(r'(?msu)(^|.)(\[(?P<attrlist>[^[\]]+?)\])?' \
+                    + r'(?:' + re.escape(lq) + r')' \
+                    + r'(?P<content>.+?)(?:'+re.escape(rq)+r')')
+        else:
+            # The text within constrained quotes must be bounded by white space.
+            # Non-word (\W) characters are allowed at boundaries to accomodate
+            # enveloping quotes and punctuation e.g. a='x', ('x'), 'x', ['x'].
+            reo = re.compile(r'(?msu)(^|[^\w;:}])(\[(?P<attrlist>[^[\]]+?)\])?' \
+                + r'(?:' + re.escape(lq) + r')' \
+                + r'(?P<content>\S|\S.*?\S)(?:'+re.escape(rq)+r')(?=\W|$)')
+        pos = 0
+        while True:
+            mo = reo.search(text,pos)
+            if not mo: break
+            if text[mo.start()] == '\\':
+                # Delete leading backslash.
+                text = text[:mo.start()] + text[mo.start()+1:]
+                # Skip past start of match.
+                pos = mo.start() + 1
+            else:
+                attrlist = {}
+                parse_attributes(mo.group('attrlist'), attrlist)
+                stag,etag = config.tag(tag, attrlist)
+                s = mo.group(1) + stag + mo.group('content') + etag
+                text = text[:mo.start()] + s + text[mo.end():]
+                pos = mo.start() + len(s)
+    return text
+
+def subs_tag(tag,dict={}):
+    """Perform attribute substitution and split tag string returning start, end
+    tag tuple (c.f. Config.tag())."""
+    if not tag:
+        return [None,None]
+    s = subs_attrs(tag,dict)
+    if not s:
+        message.warning('tag \'%s\' dropped: contains undefined attribute' % tag)
+        return [None,None]
+    result = s.split('|')
+    if len(result) == 1:
+        return result+[None]
+    elif len(result) == 2:
+        return result
+    else:
+        raise EAsciiDoc,'malformed tag: %s' % tag
+
+def parse_entry(entry, dict=None, unquote=False, unique_values=False,
+        allow_name_only=False, escape_delimiter=True):
+    """Parse name=value entry to dictionary 'dict'. Return tuple (name,value)
+    or None if illegal entry.
+    If name= then value is set to ''.
+    If name and allow_name_only=True then value is set to ''.
+    If name! and allow_name_only=True then value is set to None.
+    Leading and trailing white space is striped from 'name' and 'value'.
+    'name' can contain any printable characters.
+    If the '=' delimiter character is allowed in  the 'name' then
+    it must be escaped with a backslash and escape_delimiter must be True.
+    If 'unquote' is True leading and trailing double-quotes are stripped from
+    'name' and 'value'.
+    If unique_values' is True then dictionary entries with the same value are
+    removed before the parsed entry is added."""
+    if escape_delimiter:
+        mo = re.search(r'(?:[^\\](=))',entry)
+    else:
+        mo = re.search(r'(=)',entry)
+    if mo:  # name=value entry.
+        if mo.group(1):
+            name = entry[:mo.start(1)]
+            if escape_delimiter:
+                name = name.replace(r'\=','=')  # Unescape \= in name.
+            value = entry[mo.end(1):]
+    elif allow_name_only and entry:         # name or name! entry.
+        name = entry
+        if name[-1] == '!':
+            name = name[:-1]
+            value = None
+        else:
+            value = ''
+    else:
+        return None
+    if unquote:
+        name = strip_quotes(name)
+        if value is not None:
+            value = strip_quotes(value)
+    else:
+        name = name.strip()
+        if value is not None:
+            value = value.strip()
+    if not name:
+        return None
+    if dict is not None:
+        if unique_values:
+            for k,v in dict.items():
+                if v == value: del dict[k]
+        dict[name] = value
+    return name,value
+
+def parse_entries(entries, dict, unquote=False, unique_values=False,
+        allow_name_only=False,escape_delimiter=True):
+    """Parse name=value entries from  from lines of text in 'entries' into
+    dictionary 'dict'. Blank lines are skipped."""
+    entries = config.expand_templates(entries)
+    for entry in entries:
+        if entry and not parse_entry(entry, dict, unquote, unique_values,
+                allow_name_only, escape_delimiter):
+            raise EAsciiDoc,'malformed section entry: %s' % entry
+
+def dump_section(name,dict,f=sys.stdout):
+    """Write parameters in 'dict' as in configuration file section format with
+    section 'name'."""
+    f.write('[%s]%s' % (name,writer.newline))
+    for k,v in dict.items():
+        k = str(k)
+        k = k.replace('=',r'\=')    # Escape = in name.
+        # Quote if necessary.
+        if len(k) != len(k.strip()):
+            k = '"'+k+'"'
+        if v and len(v) != len(v.strip()):
+            v = '"'+v+'"'
+        if v is None:
+            # Don't dump undefined attributes.
+            continue
+        else:
+            s = k+'='+v
+        if s[0] == '#':
+            s = '\\' + s    # Escape so not treated as comment lines.
+        f.write('%s%s' % (s,writer.newline))
+    f.write(writer.newline)
+
+def update_attrs(attrs,dict):
+    """Update 'attrs' dictionary with parsed attributes in dictionary 'dict'."""
+    for k,v in dict.items():
+        if not is_name(k):
+            raise EAsciiDoc,'illegal attribute name: %s' % k
+        attrs[k] = v
+
+def is_attr_defined(attrs,dic):
+    """
+    Check if the sequence of attributes is defined in dictionary 'dic'.
+    Valid 'attrs' sequence syntax:
+    <attr> Return True if single attrbiute is defined.
+    <attr1>,<attr2>,... Return True if one or more attributes are defined.
+    <attr1>+<attr2>+... Return True if all the attributes are defined.
+    """
+    if OR in attrs:
+        for a in attrs.split(OR):
+            if dic.get(a.strip()) is not None:
+                return True
+        else: return False
+    elif AND in attrs:
+        for a in attrs.split(AND):
+            if dic.get(a.strip()) is None:
+                return False
+        else: return True
+    else:
+        return dic.get(attrs.strip()) is not None
+
+def filter_lines(filter_cmd, lines, attrs={}):
+    """
+    Run 'lines' through the 'filter_cmd' shell command and return the result.
+    The 'attrs' dictionary contains additional filter attributes.
+    """
+    def findfilter(name,dir,filter):
+        """Find filter file 'fname' with style name 'name' in directory
+        'dir'. Return found file path or None if not found."""
+        if name:
+            result = os.path.join(dir,'filters',name,filter)
+            if os.path.isfile(result):
+                return result
+        result = os.path.join(dir,'filters',filter)
+        if os.path.isfile(result):
+            return result
+        return None
+
+    # Return input lines if there's not filter.
+    if not filter_cmd or not filter_cmd.strip():
+        return lines
+    # Perform attributes substitution on the filter command.
+    s = subs_attrs(filter_cmd, attrs)
+    if not s:
+        message.error('undefined filter attribute in command: %s' % filter_cmd)
+        return []
+    filter_cmd = s.strip()
+    # Parse for quoted and unquoted command and command tail.
+    # Double quoted.
+    mo = re.match(r'^"(?P<cmd>[^"]+)"(?P<tail>.*)$', filter_cmd)
+    if not mo:
+        # Single quoted.
+        mo = re.match(r"^'(?P<cmd>[^']+)'(?P<tail>.*)$", filter_cmd)
+        if not mo:
+            # Unquoted catch all.
+            mo = re.match(r'^(?P<cmd>\S+)(?P<tail>.*)$', filter_cmd)
+    cmd = mo.group('cmd').strip()
+    found = None
+    if not os.path.dirname(cmd):
+        # Filter command has no directory path so search filter directories.
+        filtername = attrs.get('style')
+        d = document.attributes.get('docdir')
+        if d:
+            found = findfilter(filtername, d, cmd)
+        if not found:
+            if USER_DIR:
+                found = findfilter(filtername, USER_DIR, cmd)
+            if not found:
+                if localapp():
+                    found = findfilter(filtername, APP_DIR, cmd)
+                else:
+                    found = findfilter(filtername, CONF_DIR, cmd)
+    else:
+        if os.path.isfile(cmd):
+            found = cmd
+        else:
+            message.warning('filter not found: %s' % cmd)
+    if found:
+        filter_cmd = '"' + found + '"' + mo.group('tail')
+    if sys.platform == 'win32':
+        # Windows doesn't like running scripts directly so explicitly
+        # specify interpreter.
+        if found:
+            if cmd.endswith('.py'):
+                filter_cmd = 'python ' + filter_cmd
+            elif cmd.endswith('.rb'):
+                filter_cmd = 'ruby ' + filter_cmd
+    message.verbose('filtering: ' + filter_cmd)
+    try:
+        p = subprocess.Popen(filter_cmd, shell=True,
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        output = p.communicate(os.linesep.join(lines))[0]
+    except Exception:
+        raise EAsciiDoc,'filter error: %s: %s' % (filter_cmd, sys.exc_info()[1])
+    if output:
+        result = [s.rstrip() for s in output.split(os.linesep)]
+    else:
+        result = []
+    filter_status = p.wait()
+    if filter_status:
+        message.warning('filter non-zero exit code: %s: returned %d' %
+               (filter_cmd, filter_status))
+    if lines and not result:
+        message.warning('no output from filter: %s' % filter_cmd)
+    return result
+
+def system(name, args, is_macro=False, attrs=None):
+    """
+    Evaluate a system attribute ({name:args}) or system block macro
+    (name::[args]).
+    If is_macro is True then we are processing a system block macro otherwise
+    it's a system attribute.
+    The attrs dictionary is updated by the counter and set system attributes.
+    NOTE: The include1 attribute is used internally by the include1::[] macro
+    and is not for public use.
+    """
+    if is_macro:
+        syntax = '%s::[%s]' % (name,args)
+        separator = '\n'
+    else:
+        syntax = '{%s:%s}' % (name,args)
+        separator = writer.newline
+    if name not in ('eval','eval3','sys','sys2','sys3','include','include1','counter','counter2','set','set2','template'):
+        if is_macro:
+            msg = 'illegal system macro name: %s' % name
+        else:
+            msg = 'illegal system attribute name: %s' % name
+        message.warning(msg)
+        return None
+    if is_macro:
+        s = subs_attrs(args)
+        if s is None:
+            message.warning('skipped %s: undefined attribute in: %s' % (name,args))
+            return None
+        args = s
+    if name != 'include1':
+        message.verbose('evaluating: %s' % syntax)
+    if safe() and name not in ('include','include1'):
+        message.unsafe(syntax)
+        return None
+    result = None
+    if name in ('eval','eval3'):
+        try:
+            result = eval(args)
+            if result is True:
+                result = ''
+            elif result is False:
+                result = None
+            elif result is not None:
+                result = str(result)
+        except Exception:
+            message.warning('%s: evaluation error' % syntax)
+    elif name in ('sys','sys2','sys3'):
+        result = ''
+        fd,tmp = tempfile.mkstemp()
+        os.close(fd)
+        try:
+            cmd = args
+            cmd = cmd + (' > %s' % tmp)
+            if name == 'sys2':
+                cmd = cmd + ' 2>&1'
+            if os.system(cmd):
+                message.warning('%s: non-zero exit status' % syntax)
+            try:
+                if os.path.isfile(tmp):
+                    lines = [s.rstrip() for s in open(tmp)]
+                else:
+                    lines = []
+            except Exception:
+                raise EAsciiDoc,'%s: temp file read error' % syntax
+            result = separator.join(lines)
+        finally:
+            if os.path.isfile(tmp):
+                os.remove(tmp)
+    elif name in ('counter','counter2'):
+        mo = re.match(r'^(?P<attr>[^:]*?)(:(?P<seed>.*))?$', args)
+        attr = mo.group('attr')
+        seed = mo.group('seed')
+        if seed and (not re.match(r'^\d+$', seed) and len(seed) > 1):
+            message.warning('%s: illegal counter seed: %s' % (syntax,seed))
+            return None
+        if not is_name(attr):
+            message.warning('%s: illegal attribute name' % syntax)
+            return None
+        value = document.attributes.get(attr)
+        if value:
+            if not re.match(r'^\d+$', value) and len(value) > 1:
+                message.warning('%s: illegal counter value: %s'
+                                % (syntax,value))
+                return None
+            if re.match(r'^\d+$', value):
+                expr = value + '+1'
+            else:
+                expr = 'chr(ord("%s")+1)' % value
+            try:
+                result = str(eval(expr))
+            except Exception:
+                message.warning('%s: evaluation error: %s' % (syntax, expr))
+        else:
+            if seed:
+                result = seed
+            else:
+                result = '1'
+        document.attributes[attr] = result
+        if attrs is not None:
+            attrs[attr] = result
+        if name == 'counter2':
+            result = ''
+    elif name in ('set','set2'):
+        mo = re.match(r'^(?P<attr>[^:]*?)(:(?P<value>.*))?$', args)
+        attr = mo.group('attr')
+        value = mo.group('value')
+        if value is None:
+            value = ''
+        if attr.endswith('!'):
+            attr = attr[:-1]
+            value = None
+        if not is_name(attr):
+            message.warning('%s: illegal attribute name' % syntax)
+        else:
+            if attrs is not None:
+                attrs[attr] = value
+            if name != 'set2':  # set2 only updates local attributes.
+                document.attributes[attr] = value
+        if value is None:
+            result = None
+        else:
+            result = ''
+    elif name == 'include':
+        if not os.path.exists(args):
+            message.warning('%s: file does not exist' % syntax)
+        elif not is_safe_file(args):
+            message.unsafe(syntax)
+        else:
+            result = [s.rstrip() for s in open(args)]
+            if result:
+                result = subs_attrs(result)
+                result = separator.join(result)
+                result = result.expandtabs(reader.tabsize)
+            else:
+                result = ''
+    elif name == 'include1':
+        result = separator.join(config.include1[args])
+    elif name == 'template':
+        if not args in config.sections:
+            message.warning('%s: template does not exist' % syntax)
+        else:
+            result = []
+            for line in  config.sections[args]:
+                line = subs_attrs(line)
+                if line is not None:
+                    result.append(line)
+            result = '\n'.join(result)
+    else:
+        assert False
+    if result and name in ('eval3','sys3'):
+        macros.passthroughs.append(result)
+        result = '\x07' + str(len(macros.passthroughs)-1) + '\x07'
+    return result
+
+def subs_attrs(lines, dictionary=None):
+    """Substitute 'lines' of text with attributes from the global
+    document.attributes dictionary and from 'dictionary' ('dictionary'
+    entries take precedence). Return a tuple of the substituted lines.  'lines'
+    containing undefined attributes are deleted. If 'lines' is a string then
+    return a string.
+
+    - Attribute references are substituted in the following order: simple,
+      conditional, system.
+    - Attribute references inside 'dictionary' entry values are substituted.
+    """
+
+    def end_brace(text,start):
+        """Return index following end brace that matches brace at start in
+        text."""
+        assert text[start] == '{'
+        n = 0
+        result = start
+        for c in text[start:]:
+            # Skip braces that are followed by a backslash.
+            if result == len(text)-1 or text[result+1] != '\\':
+                if c == '{': n = n + 1
+                elif c == '}': n = n - 1
+            result = result + 1
+            if n == 0: break
+        return result
+
+    if type(lines) == str:
+        string_result = True
+        lines = [lines]
+    else:
+        string_result = False
+    if dictionary is None:
+        attrs = document.attributes
+    else:
+        # Remove numbered document attributes so they don't clash with
+        # attribute list positional attributes.
+        attrs = {}
+        for k,v in document.attributes.items():
+            if not re.match(r'^\d+$', k):
+                attrs[k] = v
+        # Substitute attribute references inside dictionary values.
+        for k,v in dictionary.items():
+            if v is None:
+                del dictionary[k]
+            else:
+                v = subs_attrs(str(v))
+                if v is None:
+                    del dictionary[k]
+                else:
+                    dictionary[k] = v
+        attrs.update(dictionary)
+    # Substitute all attributes in all lines.
+    result = []
+    for line in lines:
+        # Make it easier for regular expressions.
+        line = line.replace('\\{','{\\')
+        line = line.replace('\\}','}\\')
+        # Expand simple attributes ({name}).
+        # Nested attributes not allowed.
+        reo = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w]*?)\}(?!\\)')
+        pos = 0
+        while True:
+            mo = reo.search(line,pos)
+            if not mo: break
+            s =  attrs.get(mo.group('name'))
+            if s is None:
+                pos = mo.end()
+            else:
+                s = str(s)
+                line = line[:mo.start()] + s + line[mo.end():]
+                pos = mo.start() + len(s)
+        # Expand conditional attributes.
+        # Single name -- higher precedence.
+        reo1 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w]*?)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<value>.*?)\}(?!\\)')
+        # Multiple names (n1,n2,... or n1+n2+...) -- lower precedence.
+        reo2 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w'+OR+AND+r']*?)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<value>.*?)\}(?!\\)')
+        for reo in [reo1,reo2]:
+            pos = 0
+            while True:
+                mo = reo.search(line,pos)
+                if not mo: break
+                attr = mo.group()
+                name =  mo.group('name')
+                if reo == reo2:
+                    if OR in name:
+                        sep = OR
+                    else:
+                        sep = AND
+                    names = [s.strip() for s in name.split(sep) if s.strip() ]
+                    for n in names:
+                        if not re.match(r'^[^\\\W][-\w]*$',n):
+                            message.error('illegal attribute syntax: %s' % attr)
+                    if sep == OR:
+                        # Process OR name expression: n1,n2,...
+                        for n in names:
+                            if attrs.get(n) is not None:
+                                lval = ''
+                                break
+                        else:
+                            lval = None
+                    else:
+                        # Process AND name expression: n1+n2+...
+                        for n in names:
+                            if attrs.get(n) is None:
+                                lval = None
+                                break
+                        else:
+                            lval = ''
+                else:
+                    lval =  attrs.get(name)
+                op = mo.group('op')
+                # mo.end() not good enough because '{x={y}}' matches '{x={y}'.
+                end = end_brace(line,mo.start())
+                rval = line[mo.start('value'):end-1]
+                UNDEFINED = '{zzzzz}'
+                if lval is None:
+                    if op == '=': s = rval
+                    elif op == '?': s = ''
+                    elif op == '!': s = rval
+                    elif op == '#': s = UNDEFINED   # So the line is dropped.
+                    elif op == '%': s = rval
+                    elif op in ('@','$'):
+                        s = UNDEFINED               # So the line is dropped.
+                    else:
+                        assert False, 'illegal attribute: %s' % attr
+                else:
+                    if op == '=': s = lval
+                    elif op == '?': s = rval
+                    elif op == '!': s = ''
+                    elif op == '#': s = rval
+                    elif op == '%': s = UNDEFINED   # So the line is dropped.
+                    elif op in ('@','$'):
+                        v = re.split(r'(?<!\\):',rval)
+                        if len(v) not in (2,3):
+                            message.error('illegal attribute syntax: %s' % attr)
+                            s = ''
+                        elif not is_re('^'+v[0]+'$'):
+                            message.error('illegal attribute regexp: %s' % attr)
+                            s = ''
+                        else:
+                            v = [s.replace('\\:',':') for s in v]
+                            re_mo = re.match('^'+v[0]+'$',lval)
+                            if op == '@':
+                                if re_mo:
+                                    s = v[1]         # {<name>@<re>:<v1>[:<v2>]}
+                                else:
+                                    if len(v) == 3:   # {<name>@<re>:<v1>:<v2>}
+                                        s = v[2]
+                                    else:             # {<name>@<re>:<v1>}
+                                        s = ''
+                            else:
+                                if re_mo:
+                                    if len(v) == 2:   # {<name>$<re>:<v1>}
+                                        s = v[1]
+                                    elif v[1] == '':  # {<name>$<re>::<v2>}
+                                        s = UNDEFINED # So the line is dropped.
+                                    else:             # {<name>$<re>:<v1>:<v2>}
+                                        s = v[1]
+                                else:
+                                    if len(v) == 2:   # {<name>$<re>:<v1>}
+                                        s = UNDEFINED # So the line is dropped.
+                                    else:             # {<name>$<re>:<v1>:<v2>}
+                                        s = v[2]
+                    else:
+                        assert False, 'illegal attribute: %s' % attr
+                s = str(s)
+                line = line[:mo.start()] + s + line[end:]
+                pos = mo.start() + len(s)
+        # Drop line if it contains  unsubstituted {name} references.
+        skipped = re.search(r'(?su)\{[^\\\W][-\w]*?\}(?!\\)', line)
+        if skipped:
+            trace('dropped line', line)
+            continue;
+        # Expand system attributes (eval has precedence).
+        reos = [
+            re.compile(r'(?su)\{(?P<action>eval):(?P<expr>.*?)\}(?!\\)'),
+            re.compile(r'(?su)\{(?P<action>[^\\\W][-\w]*?):(?P<expr>.*?)\}(?!\\)'),
+        ]
+        skipped = False
+        for reo in reos:
+            pos = 0
+            while True:
+                mo = reo.search(line,pos)
+                if not mo: break
+                expr = mo.group('expr')
+                action = mo.group('action')
+                expr = expr.replace('{\\','{')
+                expr = expr.replace('}\\','}')
+                s = system(action, expr, attrs=dictionary)
+                if dictionary is not None and action in ('counter','counter2','set','set2'):
+                    # These actions create and update attributes.
+                    attrs.update(dictionary)
+                if s is None:
+                    # Drop line if the action returns None.
+                    skipped = True
+                    break
+                line = line[:mo.start()] + s + line[mo.end():]
+                pos = mo.start() + len(s)
+            if skipped:
+                break
+        if not skipped:
+            # Remove backslash from escaped entries.
+            line = line.replace('{\\','{')
+            line = line.replace('}\\','}')
+            result.append(line)
+    if string_result:
+        if result:
+            return '\n'.join(result)
+        else:
+            return None
+    else:
+        return tuple(result)
+
+def char_encoding():
+    encoding = document.attributes.get('encoding')
+    if encoding:
+        try:
+            codecs.lookup(encoding)
+        except LookupError,e:
+            raise EAsciiDoc,str(e)
+    return encoding
+
+def char_len(s):
+    return len(char_decode(s))
+
+east_asian_widths = {'W': 2,   # Wide
+                     'F': 2,   # Full-width (wide)
+                     'Na': 1,  # Narrow
+                     'H': 1,   # Half-width (narrow)
+                     'N': 1,   # Neutral (not East Asian, treated as narrow)
+                     'A': 1}   # Ambiguous (s/b wide in East Asian context,
+                               # narrow otherwise, but that doesn't work)
+"""Mapping of result codes from `unicodedata.east_asian_width()` to character
+column widths."""
+
+def column_width(s):
+    text = char_decode(s)
+    if isinstance(text, unicode):
+        width = 0
+        for c in text:
+            width += east_asian_widths[unicodedata.east_asian_width(c)]
+        return width
+    else:
+        return len(text)
+
+def char_decode(s):
+    if char_encoding():
+        try:
+            return s.decode(char_encoding())
+        except Exception:
+            raise EAsciiDoc, \
+                "'%s' codec can't decode \"%s\"" % (char_encoding(), s)
+    else:
+        return s
+
+def char_encode(s):
+    if char_encoding():
+        return s.encode(char_encoding())
+    else:
+        return s
+
+def time_str(t):
+    """Convert seconds since the Epoch to formatted local time string."""
+    t = time.localtime(t)
+    s = time.strftime('%H:%M:%S',t)
+    if time.daylight and t.tm_isdst == 1:
+        result = s + ' ' + time.tzname[1]
+    else:
+        result = s + ' ' + time.tzname[0]
+    # Attempt to convert the localtime to the output encoding.
+    try:
+        result = char_encode(result.decode(locale.getdefaultlocale()[1]))
+    except Exception:
+        pass
+    return result
+
+def date_str(t):
+    """Convert seconds since the Epoch to formatted local date string."""
+    t = time.localtime(t)
+    return time.strftime('%Y-%m-%d',t)
+
+
+class Lex:
+    """Lexical analysis routines. Static methods and attributes only."""
+    prev_element = None
+    prev_cursor = None
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def next():
+        """Returns class of next element on the input (None if EOF).  The
+        reader is assumed to be at the first line following a previous element,
+        end of file or line one.  Exits with the reader pointing to the first
+        line of the next element or EOF (leading blank lines are skipped)."""
+        reader.skip_blank_lines()
+        if reader.eof(): return None
+        # Optimization: If we've already checked for an element at this
+        # position return the element.
+        if Lex.prev_element and Lex.prev_cursor == reader.cursor:
+            return Lex.prev_element
+        if AttributeEntry.isnext():
+            result = AttributeEntry
+        elif AttributeList.isnext():
+            result = AttributeList
+        elif BlockTitle.isnext() and not tables_OLD.isnext():
+            result = BlockTitle
+        elif Title.isnext():
+            if AttributeList.style() == 'float':
+                result = FloatingTitle
+            else:
+                result = Title
+        elif macros.isnext():
+            result = macros.current
+        elif lists.isnext():
+            result = lists.current
+        elif blocks.isnext():
+            result = blocks.current
+        elif tables_OLD.isnext():
+            result = tables_OLD.current
+        elif tables.isnext():
+            result = tables.current
+        else:
+            if not paragraphs.isnext():
+                raise EAsciiDoc,'paragraph expected'
+            result = paragraphs.current
+        # Optimization: Cache answer.
+        Lex.prev_cursor = reader.cursor
+        Lex.prev_element = result
+        return result
+
+    @staticmethod
+    def canonical_subs(options):
+        """Translate composite subs values."""
+        if len(options) == 1:
+            if options[0] == 'none':
+                options = ()
+            elif options[0] == 'normal':
+                options = config.subsnormal
+            elif options[0] == 'verbatim':
+                options = config.subsverbatim
+        return options
+
+    @staticmethod
+    def subs_1(s,options):
+        """Perform substitution specified in 'options' (in 'options' order)."""
+        if not s:
+            return s
+        if document.attributes.get('plaintext') is not None:
+            options = ('specialcharacters',)
+        result = s
+        options = Lex.canonical_subs(options)
+        for o in options:
+            if o == 'specialcharacters':
+                result = config.subs_specialchars(result)
+            elif o == 'attributes':
+                result = subs_attrs(result)
+            elif o == 'quotes':
+                result = subs_quotes(result)
+            elif o == 'specialwords':
+                result = config.subs_specialwords(result)
+            elif o in ('replacements','replacements2'):
+                result = config.subs_replacements(result,o)
+            elif o == 'macros':
+                result = macros.subs(result)
+            elif o == 'callouts':
+                result = macros.subs(result,callouts=True)
+            else:
+                raise EAsciiDoc,'illegal substitution option: %s' % o
+            trace(o, s, result)
+            if not result:
+                break
+        return result
+
+    @staticmethod
+    def subs(lines,options):
+        """Perform inline processing specified by 'options' (in 'options'
+        order) on sequence of 'lines'."""
+        if not lines or not options:
+            return lines
+        options = Lex.canonical_subs(options)
+        # Join lines so quoting can span multiple lines.
+        para = '\n'.join(lines)
+        if 'macros' in options:
+            para = macros.extract_passthroughs(para)
+        for o in options:
+            if o == 'attributes':
+                # If we don't substitute attributes line-by-line then a single
+                # undefined attribute will drop the entire paragraph.
+                lines = subs_attrs(para.split('\n'))
+                para = '\n'.join(lines)
+            else:
+                para = Lex.subs_1(para,(o,))
+        if 'macros' in options:
+            para = macros.restore_passthroughs(para)
+        return para.splitlines()
+
+    @staticmethod
+    def set_margin(lines, margin=0):
+        """Utility routine that sets the left margin to 'margin' space in a
+        block of non-blank lines."""
+        # Calculate width of block margin.
+        lines = list(lines)
+        width = len(lines[0])
+        for s in lines:
+            i = re.search(r'\S',s).start()
+            if i < width: width = i
+        # Strip margin width from all lines.
+        for i in range(len(lines)):
+            lines[i] = ' '*margin + lines[i][width:]
+        return lines
+
+#---------------------------------------------------------------------------
+# Document element classes parse AsciiDoc reader input and write DocBook writer
+# output.
+#---------------------------------------------------------------------------
+class Document(object):
+
+    # doctype property.
+    def getdoctype(self):
+        return self.attributes.get('doctype')
+    def setdoctype(self,doctype):
+        self.attributes['doctype'] = doctype
+    doctype = property(getdoctype,setdoctype)
+
+    # backend property.
+    def getbackend(self):
+        return self.attributes.get('backend')
+    def setbackend(self,backend):
+        if backend:
+            backend = self.attributes.get('backend-alias-' + backend, backend)
+        self.attributes['backend'] = backend
+    backend = property(getbackend,setbackend)
+
+    def __init__(self):
+        self.infile = None      # Source file name.
+        self.outfile = None     # Output file name.
+        self.attributes = InsensitiveDict()
+        self.level = 0          # 0 => front matter. 1,2,3 => sect1,2,3.
+        self.has_errors = False # Set true if processing errors were flagged.
+        self.has_warnings = False # Set true if warnings were flagged.
+        self.safe = False       # Default safe mode.
+    def update_attributes(self,attrs=None):
+        """
+        Set implicit attributes and attributes in 'attrs'.
+        """
+        t = time.time()
+        self.attributes['localtime'] = time_str(t)
+        self.attributes['localdate'] = date_str(t)
+        self.attributes['asciidoc-version'] = VERSION
+        self.attributes['asciidoc-file'] = APP_FILE
+        self.attributes['asciidoc-dir'] = APP_DIR
+        self.attributes['asciidoc-confdir'] = CONF_DIR
+        self.attributes['user-dir'] = USER_DIR
+        if config.verbose:
+            self.attributes['verbose'] = ''
+        # Update with configuration file attributes.
+        if attrs:
+            self.attributes.update(attrs)
+        # Update with command-line attributes.
+        self.attributes.update(config.cmd_attrs)
+        # Extract miscellaneous configuration section entries from attributes.
+        if attrs:
+            config.load_miscellaneous(attrs)
+        config.load_miscellaneous(config.cmd_attrs)
+        self.attributes['newline'] = config.newline
+        # File name related attributes can't be overridden.
+        if self.infile is not None:
+            if self.infile and os.path.exists(self.infile):
+                t = os.path.getmtime(self.infile)
+            elif self.infile == '<stdin>':
+                t = time.time()
+            else:
+                t = None
+            if t:
+                self.attributes['doctime'] = time_str(t)
+                self.attributes['docdate'] = date_str(t)
+            if self.infile != '<stdin>':
+                self.attributes['infile'] = self.infile
+                self.attributes['indir'] = os.path.dirname(self.infile)
+                self.attributes['docfile'] = self.infile
+                self.attributes['docdir'] = os.path.dirname(self.infile)
+                self.attributes['docname'] = os.path.splitext(
+                        os.path.basename(self.infile))[0]
+        if self.outfile:
+            if self.outfile != '<stdout>':
+                self.attributes['outfile'] = self.outfile
+                self.attributes['outdir'] = os.path.dirname(self.outfile)
+                if self.infile == '<stdin>':
+                    self.attributes['docname'] = os.path.splitext(
+                            os.path.basename(self.outfile))[0]
+                ext = os.path.splitext(self.outfile)[1][1:]
+            elif config.outfilesuffix:
+                ext = config.outfilesuffix[1:]
+            else:
+                ext = ''
+            if ext:
+                self.attributes['filetype'] = ext
+                self.attributes['filetype-'+ext] = ''
+    def load_lang(self):
+        """
+        Load language configuration file.
+        """
+        lang = self.attributes.get('lang')
+        if lang is None:
+            filename = 'lang-en.conf'   # Default language file.
+        else:
+            filename = 'lang-' + lang + '.conf'
+        if config.load_from_dirs(filename):
+            self.attributes['lang'] = lang  # Reinstate new lang attribute.
+        else:
+            if lang is None:
+                # The default language file must exist.
+                message.error('missing conf file: %s' % filename, halt=True)
+            else:
+                message.warning('missing language conf file: %s' % filename)
+    def set_deprecated_attribute(self,old,new):
+        """
+        Ensures the 'old' name of an attribute that was renamed to 'new' is
+        still honored.
+        """
+        if self.attributes.get(new) is None:
+            if self.attributes.get(old) is not None:
+                self.attributes[new] = self.attributes[old]
+        else:
+            self.attributes[old] = self.attributes[new]
+    def consume_attributes_and_comments(self,comments_only=False,noblanks=False):
+        """
+        Returns True if one or more attributes or comments were consumed.
+        If 'noblanks' is True then consumation halts if a blank line is
+        encountered.
+        """
+        result = False
+        finished = False
+        while not finished:
+            finished = True
+            if noblanks and not reader.read_next(): return result
+            if blocks.isnext() and 'skip' in blocks.current.options:
+                result = True
+                finished = False
+                blocks.current.translate()
+            if noblanks and not reader.read_next(): return result
+            if macros.isnext() and macros.current.name == 'comment':
+                result = True
+                finished = False
+                macros.current.translate()
+            if not comments_only:
+                if AttributeEntry.isnext():
+                    result = True
+                    finished = False
+                    AttributeEntry.translate()
+                if AttributeList.isnext():
+                    result = True
+                    finished = False
+                    AttributeList.translate()
+        return result
+    def parse_header(self,doctype,backend):
+        """
+        Parses header, sets corresponding document attributes and finalizes
+        document doctype and backend properties.
+        Returns False if the document does not have a header.
+        'doctype' and 'backend' are the doctype and backend option values
+        passed on the command-line, None if no command-line option was not
+        specified.
+        """
+        assert self.level == 0
+        # Skip comments and attribute entries that preceed the header.
+        self.consume_attributes_and_comments()
+        if doctype is not None:
+            # Command-line overrides header.
+            self.doctype = doctype
+        elif self.doctype is None:
+            # Was not set on command-line or in document header.
+            self.doctype = DEFAULT_DOCTYPE
+        # Process document header.
+        has_header = (Title.isnext() and Title.level == 0
+                      and AttributeList.style() != 'float')
+        if self.doctype == 'manpage' and not has_header:
+            message.error('manpage document title is mandatory',halt=True)
+        if has_header:
+            Header.parse()
+        # Command-line entries override header derived entries.
+        self.attributes.update(config.cmd_attrs)
+        # DEPRECATED: revision renamed to revnumber.
+        self.set_deprecated_attribute('revision','revnumber')
+        # DEPRECATED: date renamed to revdate.
+        self.set_deprecated_attribute('date','revdate')
+        if doctype is not None:
+            # Command-line overrides header.
+            self.doctype = doctype
+        if backend is not None:
+            # Command-line overrides header.
+            self.backend = backend
+        elif self.backend is None:
+            # Was not set on command-line or in document header.
+            self.backend = DEFAULT_BACKEND
+        else:
+            # Has been set in document header.
+            self.backend = self.backend # Translate alias in header.
+        assert self.doctype in ('article','manpage','book'), 'illegal document type'
+        return has_header
+    def translate(self,has_header):
+        if self.doctype == 'manpage':
+            # Translate mandatory NAME section.
+            if Lex.next() is not Title:
+                message.error('name section expected')
+            else:
+                Title.translate()
+                if Title.level != 1:
+                    message.error('name section title must be at level 1')
+                if not isinstance(Lex.next(),Paragraph):
+                    message.error('malformed name section body')
+                lines = reader.read_until(r'^$')
+                s = ' '.join(lines)
+                mo = re.match(r'^(?P<manname>.*?)\s+-\s+(?P<manpurpose>.*)$',s)
+                if not mo:
+                    message.error('malformed name section body')
+                self.attributes['manname'] = mo.group('manname').strip()
+                self.attributes['manpurpose'] = mo.group('manpurpose').strip()
+                names = [s.strip() for s in self.attributes['manname'].split(',')]
+                if len(names) > 9:
+                    message.warning('to many manpage names')
+                for i,name in enumerate(names):
+                    self.attributes['manname%d' % (i+1)] = name
+        if has_header:
+            # Do postponed substitutions (backend confs have been loaded).
+            self.attributes['doctitle'] = Title.dosubs(self.attributes['doctitle'])
+            if config.header_footer:
+                hdr = config.subs_section('header',{})
+                writer.write(hdr,trace='header')
+            if 'title' in self.attributes:
+                del self.attributes['title']
+            self.consume_attributes_and_comments()
+            if self.doctype in ('article','book'):
+                # Translate 'preamble' (untitled elements between header
+                # and first section title).
+                if Lex.next() is not Title:
+                    stag,etag = config.section2tags('preamble')
+                    writer.write(stag,trace='preamble open')
+                    Section.translate_body()
+                    writer.write(etag,trace='preamble close')
+            elif self.doctype == 'manpage' and 'name' in config.sections:
+                writer.write(config.subs_section('name',{}), trace='name')
+        else:
+            self.process_author_names()
+            if config.header_footer:
+                hdr = config.subs_section('header',{})
+                writer.write(hdr,trace='header')
+            if Lex.next() is not Title:
+                Section.translate_body()
+        # Process remaining sections.
+        while not reader.eof():
+            if Lex.next() is not Title:
+                raise EAsciiDoc,'section title expected'
+            Section.translate()
+        Section.setlevel(0) # Write remaining unwritten section close tags.
+        # Substitute document parameters and write document footer.
+        if config.header_footer:
+            ftr = config.subs_section('footer',{})
+            writer.write(ftr,trace='footer')
+    def parse_author(self,s):
+        """ Return False if the author is malformed."""
+        attrs = self.attributes # Alias for readability.
+        s = s.strip()
+        mo = re.match(r'^(?P<name1>[^<>\s]+)'
+                '(\s+(?P<name2>[^<>\s]+))?'
+                '(\s+(?P<name3>[^<>\s]+))?'
+                '(\s+<(?P<email>\S+)>)?$',s)
+        if not mo:
+            # Names that don't match the formal specification.
+            if s:
+                attrs['firstname'] = s
+            return
+        firstname = mo.group('name1')
+        if mo.group('name3'):
+            middlename = mo.group('name2')
+            lastname = mo.group('name3')
+        else:
+            middlename = None
+            lastname = mo.group('name2')
+        firstname = firstname.replace('_',' ')
+        if middlename:
+            middlename = middlename.replace('_',' ')
+        if lastname:
+            lastname = lastname.replace('_',' ')
+        email = mo.group('email')
+        if firstname:
+            attrs['firstname'] = firstname
+        if middlename:
+            attrs['middlename'] = middlename
+        if lastname:
+            attrs['lastname'] = lastname
+        if email:
+            attrs['email'] = email
+        return
+    def process_author_names(self):
+        """ Calculate any missing author related attributes."""
+        attrs = self.attributes # Alias for readability.
+        firstname = attrs.get('firstname','')
+        middlename = attrs.get('middlename','')
+        lastname = attrs.get('lastname','')
+        author = attrs.get('author')
+        initials = attrs.get('authorinitials')
+        if author and not (firstname or middlename or lastname):
+            self.parse_author(author)
+            attrs['author'] = author.replace('_',' ')
+            self.process_author_names()
+            return
+        if not author:
+            author = '%s %s %s' % (firstname, middlename, lastname)
+            author = author.strip()
+            author = re.sub(r'\s+',' ', author)
+        if not initials:
+            initials = (char_decode(firstname)[:1] +
+                       char_decode(middlename)[:1] + char_decode(lastname)[:1])
+            initials = char_encode(initials).upper()
+        names = [firstname,middlename,lastname,author,initials]
+        for i,v in enumerate(names):
+            v = config.subs_specialchars(v)
+            v = subs_attrs(v)
+            names[i] = v
+        firstname,middlename,lastname,author,initials = names
+        if firstname:
+            attrs['firstname'] = firstname
+        if middlename:
+            attrs['middlename'] = middlename
+        if lastname:
+            attrs['lastname'] = lastname
+        if author:
+            attrs['author'] = author
+        if initials:
+            attrs['authorinitials'] = initials
+        if author:
+            attrs['authored'] = ''
+
+
+class Header:
+    """Static methods and attributes only."""
+    REV_LINE_RE = r'^(\D*(?P<revnumber>.*?),)?(?P<revdate>.*?)(:\s*(?P<revremark>.*))?$'
+    RCS_ID_RE = r'^\$Id: \S+ (?P<revnumber>\S+) (?P<revdate>\S+) \S+ (?P<author>\S+) (\S+ )?\$$'
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def parse():
+        assert Lex.next() is Title and Title.level == 0
+        attrs = document.attributes # Alias for readability.
+        # Postpone title subs until backend conf files have been loaded.
+        Title.translate(skipsubs=True)
+        attrs['doctitle'] = Title.attributes['title']
+        document.consume_attributes_and_comments(noblanks=True)
+        s = reader.read_next()
+        mo = None
+        if s:
+            # Process first header line after the title that is not a comment
+            # or an attribute entry.
+            s = reader.read()
+            mo = re.match(Header.RCS_ID_RE,s)
+            if not mo:
+                document.parse_author(s)
+                document.consume_attributes_and_comments(noblanks=True)
+                if reader.read_next():
+                    # Process second header line after the title that is not a
+                    # comment or an attribute entry.
+                    s = reader.read()
+                    s = subs_attrs(s)
+                    if s:
+                        mo = re.match(Header.RCS_ID_RE,s)
+                        if not mo:
+                            mo = re.match(Header.REV_LINE_RE,s)
+            document.consume_attributes_and_comments(noblanks=True)
+        s = attrs.get('revnumber')
+        if s:
+            mo = re.match(Header.RCS_ID_RE,s)
+        if mo:
+            revnumber = mo.group('revnumber')
+            if revnumber:
+                attrs['revnumber'] = revnumber.strip()
+            author = mo.groupdict().get('author')
+            if author and 'firstname' not in attrs:
+                document.parse_author(author)
+            revremark = mo.groupdict().get('revremark')
+            if revremark is not None:
+                revremark = [revremark]
+                # Revision remarks can continue on following lines.
+                while reader.read_next():
+                    if document.consume_attributes_and_comments(noblanks=True):
+                        break
+                    revremark.append(reader.read())
+                revremark = Lex.subs(revremark,['normal'])
+                revremark = '\n'.join(revremark).strip()
+                attrs['revremark'] = revremark
+            revdate = mo.group('revdate')
+            if revdate:
+                attrs['revdate'] = revdate.strip()
+            elif revnumber or revremark:
+                # Set revision date to ensure valid DocBook revision.
+                attrs['revdate'] = attrs['docdate']
+        document.process_author_names()
+        if document.doctype == 'manpage':
+            # manpage title formatted like mantitle(manvolnum).
+            mo = re.match(r'^(?P<mantitle>.*)\((?P<manvolnum>.*)\)$',
+                          attrs['doctitle'])
+            if not mo:
+                message.error('malformed manpage title')
+            else:
+                mantitle = mo.group('mantitle').strip()
+                mantitle = subs_attrs(mantitle)
+                if mantitle is None:
+                    message.error('undefined attribute in manpage title')
+                # mantitle is lowered only if in ALL CAPS
+                if mantitle == mantitle.upper():
+                    mantitle = mantitle.lower()
+                attrs['mantitle'] = mantitle;
+                attrs['manvolnum'] = mo.group('manvolnum').strip()
+
+class AttributeEntry:
+    """Static methods and attributes only."""
+    pattern = None
+    subs = None
+    name = None
+    name2 = None
+    value = None
+    attributes = {}     # Accumulates all the parsed attribute entries.
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        if not AttributeEntry.pattern:
+            pat = document.attributes.get('attributeentry-pattern')
+            if not pat:
+                message.error("[attributes] missing 'attributeentry-pattern' entry")
+            AttributeEntry.pattern = pat
+        line = reader.read_next()
+        if line:
+            # Attribute entry formatted like :<name>[.<name2>]:[ <value>]
+            mo = re.match(AttributeEntry.pattern,line)
+            if mo:
+                AttributeEntry.name = mo.group('attrname')
+                AttributeEntry.name2 = mo.group('attrname2')
+                AttributeEntry.value = mo.group('attrvalue') or ''
+                AttributeEntry.value = AttributeEntry.value.strip()
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is AttributeEntry
+        attr = AttributeEntry    # Alias for brevity.
+        reader.read()            # Discard attribute entry from reader.
+        while attr.value.endswith(' +'):
+            if not reader.read_next(): break
+            attr.value = attr.value[:-1] + reader.read().strip()
+        if attr.name2 is not None:
+            # Configuration file attribute.
+            if attr.name2 != '':
+                # Section entry attribute.
+                section = {}
+                # Some sections can have name! syntax.
+                if attr.name in ('attributes','miscellaneous') and attr.name2[-1] == '!':
+                    section[attr.name] = [attr.name2]
+                else:
+                   section[attr.name] = ['%s=%s' % (attr.name2,attr.value)]
+                config.load_sections(section)
+                config.load_miscellaneous(config.conf_attrs)
+            else:
+                # Markup template section attribute.
+                if attr.name in config.sections:
+                    config.sections[attr.name] = [attr.value]
+                else:
+                    message.warning('missing configuration section: %s' % attr.name)
+        else:
+            # Normal attribute.
+            if attr.name[-1] == '!':
+                # Names like name! undefine the attribute.
+                attr.name = attr.name[:-1]
+                attr.value = None
+            # Strip white space and illegal name chars.
+            attr.name = re.sub(r'(?u)[^\w\-_]', '', attr.name).lower()
+            # Don't override most command-line attributes.
+            if attr.name in config.cmd_attrs \
+                    and attr.name not in ('trace','numbered'):
+                return
+            # Update document attributes with attribute value.
+            if attr.value is not None:
+                mo = re.match(r'^pass:(?P<attrs>.*)\[(?P<value>.*)\]$', attr.value)
+                if mo:
+                    # Inline passthrough syntax.
+                    attr.subs = mo.group('attrs')
+                    attr.value = mo.group('value')  # Passthrough.
+                else:
+                    # Default substitution.
+                    # DEPRECATED: attributeentry-subs
+                    attr.subs = document.attributes.get('attributeentry-subs',
+                                'specialcharacters,attributes')
+                attr.subs = parse_options(attr.subs, SUBS_OPTIONS,
+                            'illegal substitution option')
+                attr.value = Lex.subs((attr.value,), attr.subs)
+                attr.value = writer.newline.join(attr.value)
+                document.attributes[attr.name] = attr.value
+            elif attr.name in document.attributes:
+                del document.attributes[attr.name]
+            attr.attributes[attr.name] = attr.value
+
+class AttributeList:
+    """Static methods and attributes only."""
+    pattern = None
+    match = None
+    attrs = {}
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def initialize():
+        if not 'attributelist-pattern' in document.attributes:
+            message.error("[attributes] missing 'attributelist-pattern' entry")
+        AttributeList.pattern = document.attributes['attributelist-pattern']
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        line = reader.read_next()
+        if line:
+            mo = re.match(AttributeList.pattern, line)
+            if mo:
+                AttributeList.match = mo
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is AttributeList
+        reader.read()   # Discard attribute list from reader.
+        attrs = {}
+        d = AttributeList.match.groupdict()
+        for k,v in d.items():
+            if v is not None:
+                if k == 'attrlist':
+                    v = subs_attrs(v)
+                    if v:
+                        parse_attributes(v, attrs)
+                else:
+                    AttributeList.attrs[k] = v
+        AttributeList.subs(attrs)
+        AttributeList.attrs.update(attrs)
+    @staticmethod
+    def subs(attrs):
+        '''Substitute single quoted attribute values normally.'''
+        reo = re.compile(r"^'.*'$")
+        for k,v in attrs.items():
+            if reo.match(str(v)):
+                attrs[k] = Lex.subs_1(v[1:-1],SUBS_NORMAL)
+    @staticmethod
+    def style():
+        return AttributeList.attrs.get('style') or AttributeList.attrs.get('1')
+    @staticmethod
+    def consume(d):
+        """Add attribute list to the dictionary 'd' and reset the
+        list."""
+        if AttributeList.attrs:
+            d.update(AttributeList.attrs)
+            AttributeList.attrs = {}
+            # Generate option attributes.
+            if 'options' in d:
+                options = parse_options(d['options'], (), 'illegal option name')
+                for option in options:
+                    d[option+'-option'] = ''
+
+class BlockTitle:
+    """Static methods and attributes only."""
+    title = None
+    pattern = None
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def isnext():
+        result = False  # Assume not next.
+        line = reader.read_next()
+        if line:
+            mo = re.match(BlockTitle.pattern,line)
+            if mo:
+                BlockTitle.title = mo.group('title')
+                result = True
+        return result
+    @staticmethod
+    def translate():
+        assert Lex.next() is BlockTitle
+        reader.read()   # Discard title from reader.
+        # Perform title substitutions.
+        if not Title.subs:
+            Title.subs = config.subsnormal
+        s = Lex.subs((BlockTitle.title,), Title.subs)
+        s = writer.newline.join(s)
+        if not s:
+            message.warning('blank block title')
+        BlockTitle.title = s
+    @staticmethod
+    def consume(d):
+        """If there is a title add it to dictionary 'd' then reset title."""
+        if BlockTitle.title:
+            d['title'] = BlockTitle.title
+            BlockTitle.title = None
+
+class Title:
+    """Processes Header and Section titles. Static methods and attributes
+    only."""
+    # Class variables
+    underlines = ('==','--','~~','^^','++') # Levels 0,1,2,3,4.
+    subs = ()
+    pattern = None
+    level = 0
+    attributes = {}
+    sectname = None
+    section_numbers = [0]*len(underlines)
+    dump_dict = {}
+    linecount = None    # Number of lines in title (1 or 2).
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def translate(skipsubs=False):
+        """Parse the Title.attributes and Title.level from the reader. The
+        real work has already been done by parse()."""
+        assert Lex.next() in (Title,FloatingTitle)
+        # Discard title from reader.
+        for i in range(Title.linecount):
+            reader.read()
+        Title.setsectname()
+        if not skipsubs:
+            Title.attributes['title'] = Title.dosubs(Title.attributes['title'])
+    @staticmethod
+    def dosubs(title):
+        """
+        Perform title substitutions.
+        """
+        if not Title.subs:
+            Title.subs = config.subsnormal
+        title = Lex.subs((title,), Title.subs)
+        title = writer.newline.join(title)
+        if not title:
+            message.warning('blank section title')
+        return title
+    @staticmethod
+    def isnext():
+        lines = reader.read_ahead(2)
+        return Title.parse(lines)
+    @staticmethod
+    def parse(lines):
+        """Parse title at start of lines tuple."""
+        if len(lines) == 0: return False
+        if len(lines[0]) == 0: return False # Title can't be blank.
+        # Check for single-line titles.
+        result = False
+        for level in range(len(Title.underlines)):
+            k = 'sect%s' % level
+            if k in Title.dump_dict:
+                mo = re.match(Title.dump_dict[k], lines[0])
+                if mo:
+                    Title.attributes = mo.groupdict()
+                    Title.level = level
+                    Title.linecount = 1
+                    result = True
+                    break
+        if not result:
+            # Check for double-line titles.
+            if not Title.pattern: return False  # Single-line titles only.
+            if len(lines) < 2: return False
+            title,ul = lines[:2]
+            title_len = column_width(title)
+            ul_len = char_len(ul)
+            if ul_len < 2: return False
+            # Fast elimination check.
+            if ul[:2] not in Title.underlines: return False
+            # Length of underline must be within +-3 of title.
+            if not ((ul_len-3 < title_len < ul_len+3)
+                    # Next test for backward compatibility.
+                    or (ul_len-3 < char_len(title) < ul_len+3)):
+                return False
+            # Check for valid repetition of underline character pairs.
+            s = ul[:2]*((ul_len+1)/2)
+            if ul != s[:ul_len]: return False
+            # Don't be fooled by back-to-back delimited blocks, require at
+            # least one alphanumeric character in title.
+            if not re.search(r'(?u)\w',title): return False
+            mo = re.match(Title.pattern, title)
+            if mo:
+                Title.attributes = mo.groupdict()
+                Title.level = list(Title.underlines).index(ul[:2])
+                Title.linecount = 2
+                result = True
+        # Check for expected pattern match groups.
+        if result:
+            if not 'title' in Title.attributes:
+                message.warning('[titles] entry has no <title> group')
+                Title.attributes['title'] = lines[0]
+            for k,v in Title.attributes.items():
+                if v is None: del Title.attributes[k]
+        try:
+            Title.level += int(document.attributes.get('leveloffset','0'))
+        except:
+            pass
+        Title.attributes['level'] = str(Title.level)
+        return result
+    @staticmethod
+    def load(entries):
+        """Load and validate [titles] section entries dictionary."""
+        if 'underlines' in entries:
+            errmsg = 'malformed [titles] underlines entry'
+            try:
+                underlines = parse_list(entries['underlines'])
+            except Exception:
+                raise EAsciiDoc,errmsg
+            if len(underlines) != len(Title.underlines):
+                raise EAsciiDoc,errmsg
+            for s in underlines:
+                if len(s) !=2:
+                    raise EAsciiDoc,errmsg
+            Title.underlines = tuple(underlines)
+            Title.dump_dict['underlines'] = entries['underlines']
+        if 'subs' in entries:
+            Title.subs = parse_options(entries['subs'], SUBS_OPTIONS,
+                'illegal [titles] subs entry')
+            Title.dump_dict['subs'] = entries['subs']
+        if 'sectiontitle' in entries:
+            pat = entries['sectiontitle']
+            if not pat or not is_re(pat):
+                raise EAsciiDoc,'malformed [titles] sectiontitle entry'
+            Title.pattern = pat
+            Title.dump_dict['sectiontitle'] = pat
+        if 'blocktitle' in entries:
+            pat = entries['blocktitle']
+            if not pat or not is_re(pat):
+                raise EAsciiDoc,'malformed [titles] blocktitle entry'
+            BlockTitle.pattern = pat
+            Title.dump_dict['blocktitle'] = pat
+        # Load single-line title patterns.
+        for k in ('sect0','sect1','sect2','sect3','sect4'):
+            if k in entries:
+                pat = entries[k]
+                if not pat or not is_re(pat):
+                    raise EAsciiDoc,'malformed [titles] %s entry' % k
+                Title.dump_dict[k] = pat
+        # TODO: Check we have either a Title.pattern or at least one
+        # single-line title pattern -- can this be done here or do we need
+        # check routine like the other block checkers?
+    @staticmethod
+    def dump():
+        dump_section('titles',Title.dump_dict)
+    @staticmethod
+    def setsectname():
+        """
+        Set Title section name:
+        If the first positional or 'template' attribute is set use it,
+        next search for section title in [specialsections],
+        if not found use default 'sect<level>' name.
+        """
+        sectname = AttributeList.attrs.get('1')
+        if sectname and sectname != 'float':
+            Title.sectname = sectname
+        elif 'template' in AttributeList.attrs:
+            Title.sectname = AttributeList.attrs['template']
+        else:
+            for pat,sect in config.specialsections.items():
+                mo = re.match(pat,Title.attributes['title'])
+                if mo:
+                    title = mo.groupdict().get('title')
+                    if title is not None:
+                        Title.attributes['title'] = title.strip()
+                    else:
+                        Title.attributes['title'] = mo.group().strip()
+                    Title.sectname = sect
+                    break
+            else:
+                Title.sectname = 'sect%d' % Title.level
+    @staticmethod
+    def getnumber(level):
+        """Return next section number at section 'level' formatted like
+        1.2.3.4."""
+        number = ''
+        for l in range(len(Title.section_numbers)):
+            n = Title.section_numbers[l]
+            if l == 0:
+                continue
+            elif l < level:
+                number = '%s%d.' % (number, n)
+            elif l == level:
+                number = '%s%d.' % (number, n + 1)
+                Title.section_numbers[l] = n + 1
+            elif l > level:
+                # Reset unprocessed section levels.
+                Title.section_numbers[l] = 0
+        return number
+
+
+class FloatingTitle(Title):
+    '''Floated titles are translated differently.'''
+    @staticmethod
+    def isnext():
+        return Title.isnext() and AttributeList.style() == 'float'
+    @staticmethod
+    def translate():
+        assert Lex.next() is FloatingTitle
+        Title.translate()
+        Section.set_id()
+        AttributeList.consume(Title.attributes)
+        template = 'floatingtitle'
+        if template in config.sections:
+            stag,etag = config.section2tags(template,Title.attributes)
+            writer.write(stag,trace='floating title')
+        else:
+            message.warning('missing template section: [%s]' % template)
+
+
+class Section:
+    """Static methods and attributes only."""
+    endtags = []  # Stack of currently open section (level,endtag) tuples.
+    ids = []      # List of already used ids.
+    def __init__(self):
+        raise AssertionError,'no class instances allowed'
+    @staticmethod
+    def savetag(level,etag):
+        """Save section end."""
+        Section.endtags.append((level,etag))
+    @staticmethod
+    def setlevel(level):
+        """Set document level and write open section close tags up to level."""
+        while Section.endtags and Section.endtags[-1][0] >= level:
+            writer.write(Section.endtags.pop()[1],trace='section close')
+        document.level = level
+    @staticmethod
+    def gen_id(title):
+        """
+        The normalized value of the id attribute is an NCName according to
+        the 'Namespaces in XML' Recommendation:
+        NCName          ::=     NCNameStartChar NCNameChar*
+        NCNameChar      ::=     NameChar - ':'
+        NCNameStartChar ::=     Letter | '_'
+        NameChar        ::=     Letter | Digit | '.' | '-' | '_' | ':'
+        """
+        # Replace non-alpha numeric characters in title with underscores and
+        # convert to lower case.
+        base_ident = char_encode(re.sub(r'(?u)\W+', '_',
+                char_decode(title)).strip('_').lower())
+        # Prefix the ID name with idprefix attribute or underscore if not
+        # defined. Prefix ensures the ID does not clash with existing IDs.
+        idprefix = document.attributes.get('idprefix','_')
+        base_ident = idprefix + base_ident
+        i = 1
+        while True:
+            if i == 1:
+                ident = base_ident
+            else:
+                ident = '%s_%d' % (base_ident, i)
+            if ident not in Section.ids:
+                Section.ids.append(ident)
+                return ident
+            else:
+                ident = base_ident
+            i += 1
+    @staticmethod
+    def set_id():
+        if not document.attributes.get('sectids') is None \
+                and 'id' not in AttributeList.attrs:
+            # Generate ids for sections.
+            AttributeList.attrs['id'] = Section.gen_id(Title.attributes['title'])
+    @staticmethod
+    def translate():
+        assert Lex.next() is Title
+        prev_sectname = Title.sectname
+        Title.translate()
+        if Title.level == 0 and document.doctype != 'book':
+            message.error('only book doctypes can contain level 0 sections')
+        if Title.level > document.level \
+                and 'basebackend-docbook' in document.attributes \
+                and prev_sectname in ('colophon','abstract', \
+                    'dedication','glossary','bibliography'):
+            message.error('%s section cannot contain sub-sections' % prev_sectname)
+        if Title.level > document.level+1:
+            # Sub-sections of multi-part book level zero Preface and Appendices
+            # are meant to be out of sequence.
+            if document.doctype == 'book' \
+                    and document.level == 0 \
+                    and Title.level == 2 \
+                    and prev_sectname in ('preface','appendix'):
+                pass
+            else:
+                message.warning('section title out of sequence: '
+                    'expected level %d, got level %d'
+                    % (document.level+1, Title.level))
+        Section.set_id()
+        Section.setlevel(Title.level)
+        if 'numbered' in document.attributes:
+            Title.attributes['sectnum'] = Title.getnumber(document.level)
+        else:
+            Title.attributes['sectnum'] = ''
+        AttributeList.consume(Title.attributes)
+        stag,etag = config.section2tags(Title.sectname,Title.attributes)
+        Section.savetag(Title.level,etag)
+        writer.write(stag,trace='section open: level %d: %s' %
+                (Title.level, Title.attributes['title']))
+        Section.translate_body()
+    @staticmethod
+    def translate_body(terminator=Title):
+        isempty = True
+        next = Lex.next()
+        while next and next is not terminator:
+            if isinstance(terminator,DelimitedBlock) and next is Title:
+                message.error('section title not permitted in delimited block')
+            next.translate()
+            next = Lex.next()
+            isempty = False
+        # The section is not empty if contains a subsection.
+        if next and isempty and Title.level > document.level:
+            isempty = False
+        # Report empty sections if invalid markup will result.
+        if isempty:
+            if document.backend == 'docbook' and Title.sectname != 'index':
+                message.error('empty section is not valid')
+
+class AbstractBlock:
+    def __init__(self):
+        # Configuration parameter names common to all blocks.
+        self.CONF_ENTRIES = ('delimiter','options','subs','presubs','postsubs',
+                             'posattrs','style','.*-style','template','filter')
+        self.start = None   # File reader cursor at start delimiter.
+        self.name=None      # Configuration file section name.
+        # Configuration parameters.
+        self.delimiter=None # Regular expression matching block delimiter.
+        self.delimiter_reo=None # Compiled delimiter.
+        self.template=None  # template section entry.
+        self.options=()     # options entry list.
+        self.presubs=None   # presubs/subs entry list.
+        self.postsubs=()    # postsubs entry list.
+        self.filter=None    # filter entry.
+        self.posattrs=()    # posattrs entry list.
+        self.style=None     # Default style.
+        self.styles=OrderedDict() # Each entry is a styles dictionary.
+        # Before a block is processed it's attributes (from it's
+        # attributes list) are merged with the block configuration parameters
+        # (by self.merge_attributes()) resulting in the template substitution
+        # dictionary (self.attributes) and the block's processing parameters
+        # (self.parameters).
+        self.attributes={}
+        # The names of block parameters.
+        self.PARAM_NAMES=('template','options','presubs','postsubs','filter')
+        self.parameters=None
+        # Leading delimiter match object.
+        self.mo=None
+    def short_name(self):
+        """ Return the text following the last dash in the section name."""
+        i = self.name.rfind('-')
+        if i == -1:
+            return self.name
+        else:
+            return self.name[i+1:]
+    def error(self, msg, cursor=None, halt=False):
+        message.error('[%s] %s' % (self.name,msg), cursor, halt)
+    def is_conf_entry(self,param):
+        """Return True if param matches an allowed configuration file entry
+        name."""
+        for s in self.CONF_ENTRIES:
+            if re.match('^'+s+'$',param):
+                return True
+        return False
+    def load(self,name,entries):
+        """Update block definition from section 'entries' dictionary."""
+        self.name = name
+        self.update_parameters(entries, self, all=True)
+    def update_parameters(self, src, dst=None, all=False):
+        """
+        Parse processing parameters from src dictionary to dst object.
+        dst defaults to self.parameters.
+        If all is True then copy src entries that aren't parameter names.
+        """
+        dst = dst or self.parameters
+        msg = '[%s] malformed entry %%s: %%s' % self.name
+        def copy(obj,k,v):
+            if isinstance(obj,dict):
+                obj[k] = v
+            else:
+                setattr(obj,k,v)
+        for k,v in src.items():
+            if not re.match(r'\d+',k) and not is_name(k):
+                raise EAsciiDoc, msg % (k,v)
+            if k == 'template':
+                if not is_name(v):
+                    raise EAsciiDoc, msg % (k,v)
+                copy(dst,k,v)
+            elif k == 'filter':
+                copy(dst,k,v)
+            elif k == 'options':
+                if isinstance(v,str):
+                    v = parse_options(v, (), msg % (k,v))
+                    # Merge with existing options.
+                    v = tuple(set(dst.options).union(set(v)))
+                copy(dst,k,v)
+            elif k in ('subs','presubs','postsubs'):
+                # Subs is an alias for presubs.
+                if k == 'subs': k = 'presubs'
+                if isinstance(v,str):
+                    v = parse_options(v, SUBS_OPTIONS, msg % (k,v))
+                copy(dst,k,v)
+            elif k == 'delimiter':
+                if v and is_re(v):
+                    copy(dst,k,v)
+                else:
+                    raise EAsciiDoc, msg % (k,v)
+            elif k == 'style':
+                if is_name(v):
+                    copy(dst,k,v)
+                else:
+                    raise EAsciiDoc, msg % (k,v)
+            elif k == 'posattrs':
+                v = parse_options(v, (), msg % (k,v))
+                copy(dst,k,v)
+            else:
+                mo = re.match(r'^(?P<style>.*)-style$',k)
+                if mo:
+                    if not v:
+                        raise EAsciiDoc, msg % (k,v)
+                    style = mo.group('style')
+                    if not is_name(style):
+                        raise EAsciiDoc, msg % (k,v)
+                    d = {}
+                    if not parse_named_attributes(v,d):
+                        raise EAsciiDoc, msg % (k,v)
+                    if 'subs' in d:
+                        # Subs is an alias for presubs.
+                        d['presubs'] = d['subs']
+                        del d['subs']
+                    self.styles[style] = d
+                elif all or k in self.PARAM_NAMES:
+                    copy(dst,k,v) # Derived class specific entries.
+    def get_param(self,name,params=None):
+        """
+        Return named processing parameter from params dictionary.
+        If the parameter is not in params look in self.parameters.
+        """
+        if params and name in params:
+            return params[name]
+        elif name in self.parameters:
+            return self.parameters[name]
+        else:
+            return None
+    def get_subs(self,params=None):
+        """
+        Return (presubs,postsubs) tuple.
+        """
+        presubs = self.get_param('presubs',params)
+        postsubs = self.get_param('postsubs',params)
+        return (presubs,postsubs)
+    def dump(self):
+        """Write block definition to stdout."""
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('['+self.name+']')
+        if self.is_conf_entry('delimiter'):
+            write('delimiter='+self.delimiter)
+        if self.template:
+            write('template='+self.template)
+        if self.options:
+            write('options='+','.join(self.options))
+        if self.presubs:
+            if self.postsubs:
+                write('presubs='+','.join(self.presubs))
+            else:
+                write('subs='+','.join(self.presubs))
+        if self.postsubs:
+            write('postsubs='+','.join(self.postsubs))
+        if self.filter:
+            write('filter='+self.filter)
+        if self.posattrs:
+            write('posattrs='+','.join(self.posattrs))
+        if self.style:
+            write('style='+self.style)
+        if self.styles:
+            for style,d in self.styles.items():
+                s = ''
+                for k,v in d.items(): s += '%s=%r,' % (k,v)
+                write('%s-style=%s' % (style,s[:-1]))
+    def validate(self):
+        """Validate block after the complete configuration has been loaded."""
+        if self.is_conf_entry('delimiter') and not self.delimiter:
+            raise EAsciiDoc,'[%s] missing delimiter' % self.name
+        if self.style:
+            if not is_name(self.style):
+                raise EAsciiDoc, 'illegal style name: %s' % self.style
+            if not self.style in self.styles:
+                if not isinstance(self,List):   # Lists don't have templates.
+                    message.warning('[%s] \'%s\' style not in %s' % (
+                        self.name,self.style,self.styles.keys()))
+        # Check all styles for missing templates.
+        all_styles_have_template = True
+        for k,v in self.styles.items():
+            t = v.get('template')
+            if t and not t in config.sections:
+                # Defer check if template name contains attributes.
+                if not re.search(r'{.+}',t):
+                    message.warning('missing template section: [%s]' % t)
+            if not t:
+                all_styles_have_template = False
+        # Check we have a valid template entry or alternatively that all the
+        # styles have templates.
+        if self.is_conf_entry('template') and not 'skip' in self.options:
+            if self.template:
+                if not self.template in config.sections:
+                    # Defer check if template name contains attributes.
+                    if not re.search(r'{.+}',self.template):
+                        message.warning('missing template section: [%s]'
+                                        % self.template)
+            elif not all_styles_have_template:
+                if not isinstance(self,List): # Lists don't have templates.
+                    message.warning('missing styles templates: [%s]' % self.name)
+    def isnext(self):
+        """Check if this block is next in document reader."""
+        result = False
+        reader.skip_blank_lines()
+        if reader.read_next():
+            if not self.delimiter_reo:
+                # Cache compiled delimiter optimization.
+                self.delimiter_reo = re.compile(self.delimiter)
+            mo = self.delimiter_reo.match(reader.read_next())
+            if mo:
+                self.mo = mo
+                result = True
+        return result
+    def translate(self):
+        """Translate block from document reader."""
+        if not self.presubs:
+            self.presubs = config.subsnormal
+        if reader.cursor:
+            self.start = reader.cursor[:]
+    def merge_attributes(self,attrs,params=[]):
+        """
+        Use the current blocks attribute list (attrs dictionary) to build a
+        dictionary of block processing parameters (self.parameters) and tag
+        substitution attributes (self.attributes).
+
+        1. Copy the default parameters (self.*) to self.parameters.
+        self.parameters are used internally to render the current block.
+        Optional params array of additional parameters.
+
+        2. Copy attrs to self.attributes. self.attributes are used for template
+        and tag substitution in the current block.
+
+        3. If a style attribute was specified update self.parameters with the
+        corresponding style parameters; if there are any style parameters
+        remaining add them to self.attributes (existing attribute list entries
+        take precedence).
+
+        4. Set named positional attributes in self.attributes if self.posattrs
+        was specified.
+
+        5. Finally self.parameters is updated with any corresponding parameters
+        specified in attrs.
+
+        """
+
+        def check_array_parameter(param):
+            # Check the parameter is a sequence type.
+            if not is_array(self.parameters[param]):
+                message.error('malformed presubs attribute: %s' %
+                        self.parameters[param])
+                # Revert to default value.
+                self.parameters[param] = getattr(self,param)
+
+        params = list(self.PARAM_NAMES) + params
+        self.attributes = {}
+        if self.style:
+            # If a default style is defined make it available in the template.
+            self.attributes['style'] = self.style
+        self.attributes.update(attrs)
+        # Calculate dynamic block parameters.
+        # Start with configuration file defaults.
+        self.parameters = AttrDict()
+        for name in params:
+            self.parameters[name] = getattr(self,name)
+        # Load the selected style attributes.
+        posattrs = self.posattrs
+        if posattrs and posattrs[0] == 'style':
+            # Positional attribute style has highest precedence.
+            style = self.attributes.get('1')
+        else:
+            style = None
+        if not style:
+            # Use explicit style attribute, fall back to default style.
+            style = self.attributes.get('style',self.style)
+        if style:
+            if not is_name(style):
+                message.error('illegal style name: %s' % style)
+                style = self.style
+            # Lists have implicit styles and do their own style checks.
+            elif style not in self.styles and not isinstance(self,List):
+                message.warning('missing style: [%s]: %s' % (self.name,style))
+                style = self.style
+            if style in self.styles:
+                self.attributes['style'] = style
+                for k,v in self.styles[style].items():
+                    if k == 'posattrs':
+                        posattrs = v
+                    elif k in params:
+                        self.parameters[k] = v
+                    elif not k in self.attributes:
+                        # Style attributes don't take precedence over explicit.
+                        self.attributes[k] = v
+        # Set named positional attributes.
+        for i,v in enumerate(posattrs):
+            if str(i+1) in self.attributes:
+                self.attributes[v] = self.attributes[str(i+1)]
+        # Override config and style attributes with attribute list attributes.
+        self.update_parameters(attrs)
+        check_array_parameter('options')
+        check_array_parameter('presubs')
+        check_array_parameter('postsubs')
+
+class AbstractBlocks:
+    """List of block definitions."""
+    PREFIX = ''         # Conf file section name prefix set in derived classes.
+    BLOCK_TYPE = None   # Block type set in derived classes.
+    def __init__(self):
+        self.current=None
+        self.blocks = []        # List of Block objects.
+        self.default = None     # Default Block.
+        self.delimiters = None  # Combined delimiters regular expression.
+    def load(self,sections):
+        """Load block definition from 'sections' dictionary."""
+        for k in sections.keys():
+            if re.match(r'^'+ self.PREFIX + r'.+$',k):
+                d = {}
+                parse_entries(sections.get(k,()),d)
+                for b in self.blocks:
+                    if b.name == k:
+                        break
+                else:
+                    b = self.BLOCK_TYPE()
+                    self.blocks.append(b)
+                try:
+                    b.load(k,d)
+                except EAsciiDoc,e:
+                    raise EAsciiDoc,'[%s] %s' % (k,str(e))
+    def dump(self):
+        for b in self.blocks:
+            b.dump()
+    def isnext(self):
+        for b in self.blocks:
+            if b.isnext():
+                self.current = b
+                return True;
+        return False
+    def validate(self):
+        """Validate the block definitions."""
+        # Validate delimiters and build combined lists delimiter pattern.
+        delimiters = []
+        for b in self.blocks:
+            assert b.__class__ is self.BLOCK_TYPE
+            b.validate()
+            if b.delimiter:
+                delimiters.append(b.delimiter)
+        self.delimiters = re_join(delimiters)
+
+class Paragraph(AbstractBlock):
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.text=None          # Text in first line of paragraph.
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('')
+    def isnext(self):
+        result = AbstractBlock.isnext(self)
+        if result:
+            self.text = self.mo.groupdict().get('text')
+        return result
+    def translate(self):
+        AbstractBlock.translate(self)
+        attrs = self.mo.groupdict().copy()
+        if 'text' in attrs: del attrs['text']
+        BlockTitle.consume(attrs)
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        reader.read()   # Discard (already parsed item first line).
+        body = reader.read_until(paragraphs.terminators)
+        body = [self.text] + list(body)
+        presubs = self.parameters.presubs
+        postsubs = self.parameters.postsubs
+        if document.attributes.get('plaintext') is None:
+            body = Lex.set_margin(body) # Move body to left margin.
+        body = Lex.subs(body,presubs)
+        template = self.parameters.template
+        template = subs_attrs(template,attrs)
+        stag = config.section2tags(template, self.attributes,skipend=True)[0]
+        if self.parameters.filter:
+            body = filter_lines(self.parameters.filter,body,self.attributes)
+        body = Lex.subs(body,postsubs)
+        etag = config.section2tags(template, self.attributes,skipstart=True)[1]
+        # Write start tag, content, end tag.
+        writer.write(dovetail_tags(stag,body,etag),trace='paragraph')
+
+class Paragraphs(AbstractBlocks):
+    """List of paragraph definitions."""
+    BLOCK_TYPE = Paragraph
+    PREFIX = 'paradef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        self.terminators=None    # List of compiled re's.
+    def initialize(self):
+        self.terminators = [
+                re.compile(r'^\+$|^$'),
+                re.compile(AttributeList.pattern),
+                re.compile(blocks.delimiters),
+                re.compile(tables.delimiters),
+                re.compile(tables_OLD.delimiters),
+            ]
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        AbstractBlocks.validate(self)
+        # Check we have a default paragraph definition, put it last in list.
+        for b in self.blocks:
+            if b.name == 'paradef-default':
+                self.blocks.append(b)
+                self.default = b
+                self.blocks.remove(b)
+                break
+        else:
+            raise EAsciiDoc,'missing section: [paradef-default]'
+
+class List(AbstractBlock):
+    NUMBER_STYLES= ('arabic','loweralpha','upperalpha','lowerroman',
+                    'upperroman')
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('type','tags')
+        self.PARAM_NAMES += ('tags',)
+        # tabledef conf file parameters.
+        self.type=None
+        self.tags=None      # Name of listtags-<tags> conf section.
+        # Calculated parameters.
+        self.tag=None       # Current tags AttrDict.
+        self.label=None     # List item label (labeled lists).
+        self.text=None      # Text in first line of list item.
+        self.index=None     # Matched delimiter 'index' group (numbered lists).
+        self.type=None      # List type ('numbered','bulleted','labeled').
+        self.ordinal=None   # Current list item ordinal number (1..)
+        self.number_style=None # Current numbered list style ('arabic'..)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('type='+self.type)
+        write('tags='+self.tags)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        tags = [self.tags]
+        tags += [s['tags'] for s in self.styles.values() if 'tags' in s]
+        for t in tags:
+            if t not in lists.tags:
+                self.error('missing section: [listtags-%s]' % t,halt=True)
+    def isnext(self):
+        result = AbstractBlock.isnext(self)
+        if result:
+            self.label = self.mo.groupdict().get('label')
+            self.text = self.mo.groupdict().get('text')
+            self.index = self.mo.groupdict().get('index')
+        return result
+    def translate_entry(self):
+        assert self.type == 'labeled'
+        entrytag = subs_tag(self.tag.entry, self.attributes)
+        labeltag = subs_tag(self.tag.label, self.attributes)
+        writer.write(entrytag[0],trace='list entry open')
+        writer.write(labeltag[0],trace='list label open')
+        # Write labels.
+        while Lex.next() is self:
+            reader.read()   # Discard (already parsed item first line).
+            writer.write_tag(self.tag.term, [self.label],
+                             self.presubs, self.attributes,trace='list term')
+            if self.text: break
+        writer.write(labeltag[1],trace='list label close')
+        # Write item text.
+        self.translate_item()
+        writer.write(entrytag[1],trace='list entry close')
+    def translate_item(self):
+        if self.type == 'callout':
+            self.attributes['coids'] = calloutmap.calloutids(self.ordinal)
+        itemtag = subs_tag(self.tag.item, self.attributes)
+        writer.write(itemtag[0],trace='list item open')
+        # Write ItemText.
+        text = reader.read_until(lists.terminators)
+        if self.text:
+            text = [self.text] + list(text)
+        if text:
+            writer.write_tag(self.tag.text, text, self.presubs, self.attributes,trace='list text')
+        # Process explicit and implicit list item continuations.
+        while True:
+            continuation = reader.read_next() == '+'
+            if continuation: reader.read()  # Discard continuation line.
+            while Lex.next() in (BlockTitle,AttributeList):
+                # Consume continued element title and attributes.
+                Lex.next().translate()
+            if not continuation and BlockTitle.title:
+                # Titled elements terminate the list.
+                break
+            next = Lex.next()
+            if next in lists.open:
+                break
+            elif isinstance(next,List):
+                next.translate()
+            elif isinstance(next,Paragraph) and 'listelement' in next.options:
+                next.translate()
+            elif continuation:
+                # This is where continued elements are processed.
+                if next is Title:
+                    message.error('section title not allowed in list item',halt=True)
+                next.translate()
+            else:
+                break
+        writer.write(itemtag[1],trace='list item close')
+
+    @staticmethod
+    def calc_style(index):
+        """Return the numbered list style ('arabic'...) of the list item index.
+        Return None if unrecognized style."""
+        if re.match(r'^\d+[\.>]$', index):
+            style = 'arabic'
+        elif re.match(r'^[ivx]+\)$', index):
+            style = 'lowerroman'
+        elif re.match(r'^[IVX]+\)$', index):
+            style = 'upperroman'
+        elif re.match(r'^[a-z]\.$', index):
+            style = 'loweralpha'
+        elif re.match(r'^[A-Z]\.$', index):
+            style = 'upperalpha'
+        else:
+            assert False
+        return style
+
+    @staticmethod
+    def calc_index(index,style):
+        """Return the ordinal number of (1...) of the list item index
+        for the given list style."""
+        def roman_to_int(roman):
+            roman = roman.lower()
+            digits = {'i':1,'v':5,'x':10}
+            result = 0
+            for i in range(len(roman)):
+                digit = digits[roman[i]]
+                # If next digit is larger this digit is negative.
+                if i+1 < len(roman) and digits[roman[i+1]] > digit:
+                    result -= digit
+                else:
+                    result += digit
+            return result
+        index = index[:-1]
+        if style == 'arabic':
+            ordinal = int(index)
+        elif style == 'lowerroman':
+            ordinal = roman_to_int(index)
+        elif style == 'upperroman':
+            ordinal = roman_to_int(index)
+        elif style == 'loweralpha':
+            ordinal = ord(index) - ord('a') + 1
+        elif style == 'upperalpha':
+            ordinal = ord(index) - ord('A') + 1
+        else:
+            assert False
+        return ordinal
+
+    def check_index(self):
+        """Check calculated self.ordinal (1,2,...) against the item number
+        in the document (self.index) and check the number style is the same as
+        the first item (self.number_style)."""
+        assert self.type in ('numbered','callout')
+        if self.index:
+            style = self.calc_style(self.index)
+            if style != self.number_style:
+                message.warning('list item style: expected %s got %s' %
+                        (self.number_style,style), offset=1)
+            ordinal = self.calc_index(self.index,style)
+            if ordinal != self.ordinal:
+                message.warning('list item index: expected %s got %s' %
+                        (self.ordinal,ordinal), offset=1)
+
+    def check_tags(self):
+        """ Check that all necessary tags are present. """
+        tags = set(Lists.TAGS)
+        if self.type != 'labeled':
+            tags = tags.difference(['entry','label','term'])
+        missing = tags.difference(self.tag.keys())
+        if missing:
+            self.error('missing tag(s): %s' % ','.join(missing), halt=True)
+    def translate(self):
+        AbstractBlock.translate(self)
+        if self.short_name() in ('bibliography','glossary','qanda'):
+            message.deprecated('old %s list syntax' % self.short_name())
+        lists.open.append(self)
+        attrs = self.mo.groupdict().copy()
+        for k in ('label','text','index'):
+            if k in attrs: del attrs[k]
+        if self.index:
+            # Set the numbering style from first list item.
+            attrs['style'] = self.calc_style(self.index)
+        BlockTitle.consume(attrs)
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs,['tags'])
+        if self.type in ('numbered','callout'):
+            self.number_style = self.attributes.get('style')
+            if self.number_style not in self.NUMBER_STYLES:
+                message.error('illegal numbered list style: %s' % self.number_style)
+                # Fall back to default style.
+                self.attributes['style'] = self.number_style = self.style
+        self.tag = lists.tags[self.parameters.tags]
+        self.check_tags()
+        if 'width' in self.attributes:
+            # Set horizontal list 'labelwidth' and 'itemwidth' attributes.
+            v = str(self.attributes['width'])
+            mo = re.match(r'^(\d{1,2})%?$',v)
+            if mo:
+                labelwidth = int(mo.group(1))
+                self.attributes['labelwidth'] = str(labelwidth)
+                self.attributes['itemwidth'] = str(100-labelwidth)
+            else:
+                self.error('illegal attribute value: width="%s"' % v)
+        stag,etag = subs_tag(self.tag.list, self.attributes)
+        if stag:
+            writer.write(stag,trace='list open')
+        self.ordinal = 0
+        # Process list till list syntax changes or there is a new title.
+        while Lex.next() is self and not BlockTitle.title:
+            self.ordinal += 1
+            document.attributes['listindex'] = str(self.ordinal)
+            if self.type in ('numbered','callout'):
+                self.check_index()
+            if self.type in ('bulleted','numbered','callout'):
+                reader.read()   # Discard (already parsed item first line).
+                self.translate_item()
+            elif self.type == 'labeled':
+                self.translate_entry()
+            else:
+                raise AssertionError,'illegal [%s] list type' % self.name
+        if etag:
+            writer.write(etag,trace='list close')
+        if self.type == 'callout':
+            calloutmap.validate(self.ordinal)
+            calloutmap.listclose()
+        lists.open.pop()
+        if len(lists.open):
+            document.attributes['listindex'] = str(lists.open[-1].ordinal)
+
+class Lists(AbstractBlocks):
+    """List of List objects."""
+    BLOCK_TYPE = List
+    PREFIX = 'listdef-'
+    TYPES = ('bulleted','numbered','labeled','callout')
+    TAGS = ('list', 'entry','item','text', 'label','term')
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        self.open = []  # A stack of the current and parent lists.
+        self.tags={}    # List tags dictionary. Each entry is a tags AttrDict.
+        self.terminators=None    # List of compiled re's.
+    def initialize(self):
+        self.terminators = [
+                re.compile(r'^\+$|^$'),
+                re.compile(AttributeList.pattern),
+                re.compile(lists.delimiters),
+                re.compile(blocks.delimiters),
+                re.compile(tables.delimiters),
+                re.compile(tables_OLD.delimiters),
+            ]
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+        self.load_tags(sections)
+    def load_tags(self,sections):
+        """
+        Load listtags-* conf file sections to self.tags.
+        """
+        for section in sections.keys():
+            mo = re.match(r'^listtags-(?P<name>\w+)$',section)
+            if mo:
+                name = mo.group('name')
+                if name in self.tags:
+                    d = self.tags[name]
+                else:
+                    d = AttrDict()
+                parse_entries(sections.get(section,()),d)
+                for k in d.keys():
+                    if k not in self.TAGS:
+                        message.warning('[%s] contains illegal list tag: %s' %
+                                (section,k))
+                self.tags[name] = d
+    def validate(self):
+        AbstractBlocks.validate(self)
+        for b in self.blocks:
+            # Check list has valid type.
+            if not b.type in Lists.TYPES:
+                raise EAsciiDoc,'[%s] illegal type' % b.name
+            b.validate()
+    def dump(self):
+        AbstractBlocks.dump(self)
+        for k,v in self.tags.items():
+            dump_section('listtags-'+k, v)
+
+
+class DelimitedBlock(AbstractBlock):
+    def __init__(self):
+        AbstractBlock.__init__(self)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('')
+    def isnext(self):
+        return AbstractBlock.isnext(self)
+    def translate(self):
+        AbstractBlock.translate(self)
+        reader.read()   # Discard delimiter.
+        attrs = {}
+        if self.short_name() != 'comment':
+            BlockTitle.consume(attrs)
+            AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        options = self.parameters.options
+        if 'skip' in options:
+            reader.read_until(self.delimiter,same_file=True)
+        elif safe() and self.name == 'blockdef-backend':
+            message.unsafe('Backend Block')
+            reader.read_until(self.delimiter,same_file=True)
+        else:
+            template = self.parameters.template
+            template = subs_attrs(template,attrs)
+            name = self.short_name()+' block'
+            if 'sectionbody' in options:
+                # The body is treated like a section body.
+                stag,etag = config.section2tags(template,self.attributes)
+                writer.write(stag,trace=name+' open')
+                Section.translate_body(self)
+                writer.write(etag,trace=name+' close')
+            else:
+                stag = config.section2tags(template,self.attributes,skipend=True)[0]
+                body = reader.read_until(self.delimiter,same_file=True)
+                presubs = self.parameters.presubs
+                postsubs = self.parameters.postsubs
+                body = Lex.subs(body,presubs)
+                if self.parameters.filter:
+                    body = filter_lines(self.parameters.filter,body,self.attributes)
+                body = Lex.subs(body,postsubs)
+                # Write start tag, content, end tag.
+                etag = config.section2tags(template,self.attributes,skipstart=True)[1]
+                writer.write(dovetail_tags(stag,body,etag),trace=name)
+            trace(self.short_name()+' block close',etag)
+        if reader.eof():
+            self.error('missing closing delimiter',self.start)
+        else:
+            delimiter = reader.read()   # Discard delimiter line.
+            assert re.match(self.delimiter,delimiter)
+
+class DelimitedBlocks(AbstractBlocks):
+    """List of delimited blocks."""
+    BLOCK_TYPE = DelimitedBlock
+    PREFIX = 'blockdef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+    def load(self,sections):
+        """Update blocks defined in 'sections' dictionary."""
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        AbstractBlocks.validate(self)
+
+class Column:
+    """Table column."""
+    def __init__(self, width=None, align_spec=None, style=None):
+        self.width = width or '1'
+        self.halign, self.valign = Table.parse_align_spec(align_spec)
+        self.style = style      # Style name or None.
+        # Calculated attribute values.
+        self.abswidth = None    # 1..   (page units).
+        self.pcwidth = None     # 1..99 (percentage).
+
+class Cell:
+    def __init__(self, data, span_spec=None, align_spec=None, style=None):
+        self.data = data
+        self.span, self.vspan = Table.parse_span_spec(span_spec)
+        self.halign, self.valign = Table.parse_align_spec(align_spec)
+        self.style = style
+    def __repr__(self):
+        return '<Cell: %d.%d %s.%s %s "%s">' % (
+                self.span, self.vspan,
+                self.halign, self.valign,
+                self.style or '',
+                self.data)
+
+class Table(AbstractBlock):
+    ALIGN = {'<':'left', '>':'right', '^':'center'}
+    VALIGN = {'<':'top', '>':'bottom', '^':'middle'}
+    FORMATS = ('psv','csv','dsv')
+    SEPARATORS = dict(
+        csv=',',
+        dsv=r':|\n',
+        # The count and align group matches are not exact.
+        psv=r'((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<style>[a-z])?)?\|'
+    )
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('format','tags','separator')
+        # tabledef conf file parameters.
+        self.format='psv'
+        self.separator=None
+        self.tags=None          # Name of tabletags-<tags> conf section.
+        # Calculated parameters.
+        self.abswidth=None      # 1..   (page units).
+        self.pcwidth = None     # 1..99 (percentage).
+        self.rows=[]            # Parsed rows, each row is a list of Cells.
+        self.columns=[]         # List of Columns.
+    @staticmethod
+    def parse_align_spec(align_spec):
+        """
+        Parse AsciiDoc cell alignment specifier and return 2-tuple with
+        horizonatal and vertical alignment names. Unspecified alignments
+        set to None.
+        """
+        result = (None, None)
+        if align_spec:
+            mo = re.match(r'^([<\^>])?(\.([<\^>]))?$', align_spec)
+            if mo:
+                result = (Table.ALIGN.get(mo.group(1)),
+                          Table.VALIGN.get(mo.group(3)))
+        return result
+    @staticmethod
+    def parse_span_spec(span_spec):
+        """
+        Parse AsciiDoc cell span specifier and return 2-tuple with horizonatal
+        and vertical span counts. Set default values (1,1) if not
+        specified.
+        """
+        result = (None, None)
+        if span_spec:
+            mo = re.match(r'^(\d+)?(\.(\d+))?$', span_spec)
+            if mo:
+                result = (mo.group(1) and int(mo.group(1)),
+                          mo.group(3) and int(mo.group(3)))
+        return (result[0] or 1, result[1] or 1)
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('format='+self.format)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        if self.format not in Table.FORMATS:
+            self.error('illegal format=%s' % self.format,halt=True)
+        self.tags = self.tags or 'default'
+        tags = [self.tags]
+        tags += [s['tags'] for s in self.styles.values() if 'tags' in s]
+        for t in tags:
+            if t not in tables.tags:
+                self.error('missing section: [tabletags-%s]' % t,halt=True)
+        if self.separator:
+            # Evaluate escape characters.
+            self.separator = eval('"'+self.separator+'"')
+        #TODO: Move to class Tables
+        # Check global table parameters.
+        elif config.pagewidth is None:
+            self.error('missing [miscellaneous] entry: pagewidth')
+        elif config.pageunits is None:
+            self.error('missing [miscellaneous] entry: pageunits')
+    def validate_attributes(self):
+        """Validate and parse table attributes."""
+        # Set defaults.
+        format = self.format
+        tags = self.tags
+        separator = self.separator
+        abswidth = float(config.pagewidth)
+        pcwidth = 100.0
+        for k,v in self.attributes.items():
+            if k == 'format':
+                if v not in self.FORMATS:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    format = v
+            elif k == 'tags':
+                if v not in tables.tags:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    tags = v
+            elif k == 'separator':
+                separator = v
+            elif k == 'width':
+                if not re.match(r'^\d{1,3}%$',v) or int(v[:-1]) > 100:
+                    self.error('illegal %s=%s' % (k,v))
+                else:
+                    abswidth = float(v[:-1])/100 * config.pagewidth
+                    pcwidth = float(v[:-1])
+        # Calculate separator if it has not been specified.
+        if not separator:
+            separator = Table.SEPARATORS[format]
+        if format == 'csv':
+            if len(separator) > 1:
+                self.error('illegal csv separator=%s' % separator)
+                separator = ','
+        else:
+            if not is_re(separator):
+                self.error('illegal regular expression: separator=%s' %
+                        separator)
+        self.parameters.format = format
+        self.parameters.tags = tags
+        self.parameters.separator = separator
+        self.abswidth = abswidth
+        self.pcwidth = pcwidth
+    def get_tags(self,params):
+        tags = self.get_param('tags',params)
+        assert(tags and tags in tables.tags)
+        return tables.tags[tags]
+    def get_style(self,prefix):
+        """
+        Return the style dictionary whose name starts with 'prefix'.
+        """
+        if prefix is None:
+            return None
+        names = self.styles.keys()
+        names.sort()
+        for name in names:
+            if name.startswith(prefix):
+                return self.styles[name]
+        else:
+            self.error('missing style: %s*' % prefix)
+            return None
+    def parse_cols(self, cols, halign, valign):
+        """
+        Build list of column objects from table 'cols', 'halign' and 'valign'
+        attributes.
+        """
+        # [<multiplier>*][<align>][<width>][<style>]
+        COLS_RE1 = r'^((?P<count>\d+)\*)?(?P<align>[<\^>.]{,3})?(?P<width>\d+%?)?(?P<style>[a-z]\w*)?$'
+        # [<multiplier>*][<width>][<align>][<style>]
+        COLS_RE2 = r'^((?P<count>\d+)\*)?(?P<width>\d+%?)?(?P<align>[<\^>.]{,3})?(?P<style>[a-z]\w*)?$'
+        reo1 = re.compile(COLS_RE1)
+        reo2 = re.compile(COLS_RE2)
+        cols = str(cols)
+        if re.match(r'^\d+$',cols):
+            for i in range(int(cols)):
+                self.columns.append(Column())
+        else:
+            for col in re.split(r'\s*,\s*',cols):
+                mo = reo1.match(col)
+                if not mo:
+                    mo = reo2.match(col)
+                if mo:
+                    count = int(mo.groupdict().get('count') or 1)
+                    for i in range(count):
+                        self.columns.append(
+                            Column(mo.group('width'), mo.group('align'),
+                                   self.get_style(mo.group('style')))
+                        )
+                else:
+                    self.error('illegal column spec: %s' % col,self.start)
+        # Set column (and indirectly cell) default alignments.
+        for col in self.columns:
+            col.halign = col.halign or halign or document.attributes.get('halign') or 'left'
+            col.valign = col.valign or valign or document.attributes.get('valign') or 'top'
+        # Validate widths and calculate missing widths.
+        n = 0; percents = 0; props = 0
+        for col in self.columns:
+            if col.width:
+                if col.width[-1] == '%': percents += int(col.width[:-1])
+                else: props += int(col.width)
+                n += 1
+        if percents > 0 and props > 0:
+            self.error('mixed percent and proportional widths: %s'
+                    % cols,self.start)
+        pcunits = percents > 0
+        # Fill in missing widths.
+        if n < len(self.columns) and percents < 100:
+            if pcunits:
+                width = float(100 - percents)/float(len(self.columns) - n)
+            else:
+                width = 1
+            for col in self.columns:
+                if not col.width:
+                    if pcunits:
+                        col.width = str(int(width))+'%'
+                        percents += width
+                    else:
+                        col.width = str(width)
+                        props += width
+        # Calculate column alignment and absolute and percent width values.
+        percents = 0
+        for col in self.columns:
+            if pcunits:
+                col.pcwidth = float(col.width[:-1])
+            else:
+                col.pcwidth = (float(col.width)/props)*100
+            col.abswidth = self.abswidth * (col.pcwidth/100)
+            if config.pageunits in ('cm','mm','in','em'):
+                col.abswidth = '%.2f' % round(col.abswidth,2)
+            else:
+                col.abswidth = '%d' % round(col.abswidth)
+            percents += col.pcwidth
+            col.pcwidth = int(col.pcwidth)
+        if round(percents) > 100:
+            self.error('total width exceeds 100%%: %s' % cols,self.start)
+        elif round(percents) < 100:
+            self.error('total width less than 100%%: %s' % cols,self.start)
+    def build_colspecs(self):
+        """
+        Generate column related substitution attributes.
+        """
+        cols = []
+        i = 1
+        for col in self.columns:
+            colspec = self.get_tags(col.style).colspec
+            if colspec:
+                self.attributes['halign'] = col.halign
+                self.attributes['valign'] = col.valign
+                self.attributes['colabswidth'] = col.abswidth
+                self.attributes['colpcwidth'] = col.pcwidth
+                self.attributes['colnumber'] = str(i)
+                s = subs_attrs(colspec, self.attributes)
+                if not s:
+                    message.warning('colspec dropped: contains undefined attribute')
+                else:
+                    cols.append(s)
+            i += 1
+        if cols:
+            self.attributes['colspecs'] = writer.newline.join(cols)
+    def parse_rows(self, text):
+        """
+        Parse the table source text into self.rows (a list of rows, each row
+        is a list of Cells.
+        """
+        reserved = {}  # Cols reserved by rowspans (indexed by row number).
+        if self.parameters.format in ('psv','dsv'):
+            ri = 0  # Current row index 0..
+            cells = self.parse_psv_dsv(text)
+            row = []
+            ci = 0  # Column counter 0..colcount
+            for cell in cells:
+                colcount = len(self.columns) - reserved.get(ri,0)
+                if cell.vspan > 1:
+                    # Reserve spanned columns from ensuing rows.
+                    for i in range(1, cell.vspan):
+                        reserved[ri+i] = reserved.get(ri+i, 0) + cell.span
+                ci += cell.span
+                if ci <= colcount:
+                    row.append(cell)
+                if ci >= colcount:
+                    self.rows.append(row)
+                    ri += 1
+                    row = []
+                    ci = 0
+                if ci > colcount:
+                    message.warning('table row %d: span exceeds number of columns'
+                            % ri)
+        elif self.parameters.format == 'csv':
+            self.rows = self.parse_csv(text)
+        else:
+            assert True,'illegal table format'
+        # Check that all row spans match.
+        for ri,row in enumerate(self.rows):
+            row_span = 0
+            for cell in row:
+                row_span += cell.span
+            row_span += reserved.get(ri,0)
+            if ri == 0:
+                header_span = row_span
+            if row_span < header_span:
+                message.warning('table row %d: does not span all columns' % (ri+1))
+            if row_span > header_span:
+                message.warning('table row %d: exceeds columns span' % (ri+1))
+        # Check that now row spans exceed the number of rows.
+        if len([x for x in reserved.keys() if x >= len(self.rows)]) > 0:
+            message.warning('one or more cell spans exceed the available rows')
+    def subs_rows(self, rows, rowtype='body'):
+        """
+        Return a string of output markup from a list of rows, each row
+        is a list of raw data text.
+        """
+        tags = tables.tags[self.parameters.tags]
+        if rowtype == 'header':
+            rtag = tags.headrow
+        elif rowtype == 'footer':
+            rtag = tags.footrow
+        else:
+            rtag = tags.bodyrow
+        result = []
+        stag,etag = subs_tag(rtag,self.attributes)
+        for row in rows:
+            result.append(stag)
+            result += self.subs_row(row,rowtype)
+            result.append(etag)
+        return writer.newline.join(result)
+    def subs_row(self, row, rowtype):
+        """
+        Substitute the list of Cells using the data tag.
+        Returns a list of marked up table cell elements.
+        """
+        result = []
+        i = 0
+        for cell in row:
+            if i >= len(self.columns):
+                break   # Skip cells outside the header width.
+            col = self.columns[i]
+            self.attributes['halign'] = cell.halign or col.halign
+            self.attributes['valign'] = cell.valign or  col.valign
+            self.attributes['colabswidth'] = col.abswidth
+            self.attributes['colpcwidth'] = col.pcwidth
+            self.attributes['colnumber'] = str(i+1)
+            self.attributes['colspan'] = str(cell.span)
+            self.attributes['colstart'] = self.attributes['colnumber']
+            self.attributes['colend'] = str(i+cell.span)
+            self.attributes['rowspan'] = str(cell.vspan)
+            self.attributes['morerows'] = str(cell.vspan-1)
+            # Fill missing column data with blanks.
+            if i > len(self.columns) - 1:
+                data = ''
+            else:
+                data = cell.data
+            if rowtype == 'header':
+                # Use table style unless overriden by cell style.
+                colstyle = cell.style
+            else:
+                # If the cell style is not defined use the column style.
+                colstyle = cell.style or col.style
+            tags = self.get_tags(colstyle)
+            presubs,postsubs = self.get_subs(colstyle)
+            data = [data]
+            data = Lex.subs(data, presubs)
+            data = filter_lines(self.get_param('filter',colstyle),
+                                data, self.attributes)
+            data = Lex.subs(data, postsubs)
+            if rowtype != 'header':
+                ptag = tags.paragraph
+                if ptag:
+                    stag,etag = subs_tag(ptag,self.attributes)
+                    text = '\n'.join(data).strip()
+                    data = []
+                    for para in re.split(r'\n{2,}',text):
+                        data += dovetail_tags([stag],para.split('\n'),[etag])
+            if rowtype == 'header':
+                dtag = tags.headdata
+            elif rowtype == 'footer':
+                dtag = tags.footdata
+            else:
+                dtag = tags.bodydata
+            stag,etag = subs_tag(dtag,self.attributes)
+            result = result + dovetail_tags([stag],data,[etag])
+            i += cell.span
+        return result
+    def parse_csv(self,text):
+        """
+        Parse the table source text and return a list of rows, each row
+        is a list of Cells.
+        """
+        import StringIO
+        import csv
+        rows = []
+        rdr = csv.reader(StringIO.StringIO('\r\n'.join(text)),
+                     delimiter=self.parameters.separator, skipinitialspace=True)
+        try:
+            for row in rdr:
+                rows.append([Cell(data) for data in row])
+        except Exception:
+            self.error('csv parse error: %s' % row)
+        return rows
+    def parse_psv_dsv(self,text):
+        """
+        Parse list of PSV or DSV table source text lines and return a list of
+        Cells.
+        """
+        def append_cell(data, span_spec, op, align_spec, style):
+            op = op or '+'
+            if op == '*':   # Cell multiplier.
+                span = Table.parse_span_spec(span_spec)[0]
+                for i in range(span):
+                    cells.append(Cell(data, '1', align_spec, style))
+            elif op == '+': # Column spanner.
+                cells.append(Cell(data, span_spec, align_spec, style))
+            else:
+                self.error('illegal table cell operator')
+        text = '\n'.join(text)
+        separator = '(?msu)'+self.parameters.separator
+        format = self.parameters.format
+        start = 0
+        span = None
+        op = None
+        align = None
+        style = None
+        cells = []
+        data = ''
+        for mo in re.finditer(separator,text):
+            data += text[start:mo.start()]
+            if data.endswith('\\'):
+                data = data[:-1]+mo.group() # Reinstate escaped separators.
+            else:
+                append_cell(data, span, op, align, style)
+                span = mo.groupdict().get('span')
+                op = mo.groupdict().get('op')
+                align = mo.groupdict().get('align')
+                style = mo.groupdict().get('style')
+                if style:
+                    style = self.get_style(style)
+                data = ''
+            start = mo.end()
+        # Last cell follows final separator.
+        data += text[start:]
+        append_cell(data, span, op, align, style)
+        # We expect a dummy blank item preceeding first PSV cell.
+        if format == 'psv':
+            if cells[0].data.strip() != '':
+                self.error('missing leading separator: %s' % separator,
+                        self.start)
+            else:
+                cells.pop(0)
+        return cells
+    def translate(self):
+        AbstractBlock.translate(self)
+        reader.read()   # Discard delimiter.
+        # Reset instance specific properties.
+        self.columns = []
+        self.rows = []
+        attrs = {}
+        BlockTitle.consume(attrs)
+        # Mix in document attribute list.
+        AttributeList.consume(attrs)
+        self.merge_attributes(attrs)
+        self.validate_attributes()
+        # Add global and calculated configuration parameters.
+        self.attributes['pagewidth'] = config.pagewidth
+        self.attributes['pageunits'] = config.pageunits
+        self.attributes['tableabswidth'] = int(self.abswidth)
+        self.attributes['tablepcwidth'] = int(self.pcwidth)
+        # Read the entire table.
+        text = reader.read_until(self.delimiter)
+        if reader.eof():
+            self.error('missing closing delimiter',self.start)
+        else:
+            delimiter = reader.read()   # Discard closing delimiter.
+            assert re.match(self.delimiter,delimiter)
+        if len(text) == 0:
+            message.warning('[%s] table is empty' % self.name)
+            return
+        cols = attrs.get('cols')
+        if not cols:
+            # Calculate column count from number of items in first line.
+            if self.parameters.format == 'csv':
+                cols = text[0].count(self.parameters.separator) + 1
+            else:
+                cols = 0
+                for cell in self.parse_psv_dsv(text[:1]):
+                    cols += cell.span
+        self.parse_cols(cols, attrs.get('halign'), attrs.get('valign'))
+        # Set calculated attributes.
+        self.attributes['colcount'] = len(self.columns)
+        self.build_colspecs()
+        self.parse_rows(text)
+        # The 'rowcount' attribute is used by the experimental LaTeX backend.
+        self.attributes['rowcount'] = str(len(self.rows))
+        # Generate headrows, footrows, bodyrows.
+        # Headrow, footrow and bodyrow data replaces same named attributes in
+        # the table markup template. In order to ensure this data does not get
+        # a second attribute substitution (which would interfere with any
+        # already substituted inline passthroughs) unique placeholders are used
+        # (the tab character does not appear elsewhere since it is expanded on
+        # input) which are replaced after template attribute substitution.
+        headrows = footrows = bodyrows = None
+        if self.rows and 'header' in self.parameters.options:
+            headrows = self.subs_rows(self.rows[0:1],'header')
+            self.attributes['headrows'] = '\x07headrows\x07'
+            self.rows = self.rows[1:]
+        if self.rows and 'footer' in self.parameters.options:
+            footrows = self.subs_rows( self.rows[-1:], 'footer')
+            self.attributes['footrows'] = '\x07footrows\x07'
+            self.rows = self.rows[:-1]
+        if self.rows:
+            bodyrows = self.subs_rows(self.rows)
+            self.attributes['bodyrows'] = '\x07bodyrows\x07'
+        table = subs_attrs(config.sections[self.parameters.template],
+                           self.attributes)
+        table = writer.newline.join(table)
+        # Before we finish replace the table head, foot and body place holders
+        # with the real data.
+        if headrows:
+            table = table.replace('\x07headrows\x07', headrows, 1)
+        if footrows:
+            table = table.replace('\x07footrows\x07', footrows, 1)
+        if bodyrows:
+            table = table.replace('\x07bodyrows\x07', bodyrows, 1)
+        writer.write(table,trace='table')
+
+class Tables(AbstractBlocks):
+    """List of tables."""
+    BLOCK_TYPE = Table
+    PREFIX = 'tabledef-'
+    TAGS = ('colspec', 'headrow','footrow','bodyrow',
+            'headdata','footdata', 'bodydata','paragraph')
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+        # Table tags dictionary. Each entry is a tags dictionary.
+        self.tags={}
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+        self.load_tags(sections)
+    def load_tags(self,sections):
+        """
+        Load tabletags-* conf file sections to self.tags.
+        """
+        for section in sections.keys():
+            mo = re.match(r'^tabletags-(?P<name>\w+)$',section)
+            if mo:
+                name = mo.group('name')
+                if name in self.tags:
+                    d = self.tags[name]
+                else:
+                    d = AttrDict()
+                parse_entries(sections.get(section,()),d)
+                for k in d.keys():
+                    if k not in self.TAGS:
+                        message.warning('[%s] contains illegal table tag: %s' %
+                                (section,k))
+                self.tags[name] = d
+    def validate(self):
+        AbstractBlocks.validate(self)
+        # Check we have a default table definition,
+        for i in range(len(self.blocks)):
+            if self.blocks[i].name == 'tabledef-default':
+                default = self.blocks[i]
+                break
+        else:
+            raise EAsciiDoc,'missing section: [tabledef-default]'
+        # Propagate defaults to unspecified table parameters.
+        for b in self.blocks:
+            if b is not default:
+                if b.format is None: b.format = default.format
+                if b.template is None: b.template = default.template
+        # Check tags and propagate default tags.
+        if not 'default' in self.tags:
+            raise EAsciiDoc,'missing section: [tabletags-default]'
+        default = self.tags['default']
+        for tag in ('bodyrow','bodydata','paragraph'): # Mandatory default tags.
+            if tag not in default:
+                raise EAsciiDoc,'missing [tabletags-default] entry: %s' % tag
+        for t in self.tags.values():
+            if t is not default:
+                if t.colspec is None: t.colspec = default.colspec
+                if t.headrow is None: t.headrow = default.headrow
+                if t.footrow is None: t.footrow = default.footrow
+                if t.bodyrow is None: t.bodyrow = default.bodyrow
+                if t.headdata is None: t.headdata = default.headdata
+                if t.footdata is None: t.footdata = default.footdata
+                if t.bodydata is None: t.bodydata = default.bodydata
+                if t.paragraph is None: t.paragraph = default.paragraph
+        # Use body tags if header and footer tags are not specified.
+        for t in self.tags.values():
+            if not t.headrow: t.headrow = t.bodyrow
+            if not t.footrow: t.footrow = t.bodyrow
+            if not t.headdata: t.headdata = t.bodydata
+            if not t.footdata: t.footdata = t.bodydata
+        # Check table definitions are valid.
+        for b in self.blocks:
+            b.validate()
+    def dump(self):
+        AbstractBlocks.dump(self)
+        for k,v in self.tags.items():
+            dump_section('tabletags-'+k, v)
+
+class Macros:
+    # Default system macro syntax.
+    SYS_RE = r'(?u)^(?P<name>[\\]?\w(\w|-)*?)::(?P<target>\S*?)' + \
+             r'(\[(?P<attrlist>.*?)\])$'
+    def __init__(self):
+        self.macros = []        # List of Macros.
+        self.current = None     # The last matched block macro.
+        self.passthroughs = []
+        # Initialize default system macro.
+        m = Macro()
+        m.pattern = self.SYS_RE
+        m.prefix = '+'
+        m.reo = re.compile(m.pattern)
+        self.macros.append(m)
+    def load(self,entries):
+        for entry in entries:
+            m = Macro()
+            m.load(entry)
+            if m.name is None:
+                # Delete undefined macro.
+                for i,m2 in enumerate(self.macros):
+                    if m2.pattern == m.pattern:
+                        del self.macros[i]
+                        break
+                else:
+                    message.warning('unable to delete missing macro: %s' % m.pattern)
+            else:
+                # Check for duplicates.
+                for m2 in self.macros:
+                    if m2.pattern == m.pattern:
+                        message.verbose('macro redefinition: %s%s' % (m.prefix,m.name))
+                        break
+                else:
+                    self.macros.append(m)
+    def dump(self):
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('[macros]')
+        # Dump all macros except the first (built-in system) macro.
+        for m in self.macros[1:]:
+            # Escape = in pattern.
+            macro = '%s=%s%s' % (m.pattern.replace('=',r'\='), m.prefix, m.name)
+            if m.subslist is not None:
+                macro += '[' + ','.join(m.subslist) + ']'
+            write(macro)
+        write('')
+    def validate(self):
+        # Check all named sections exist.
+        if config.verbose:
+            for m in self.macros:
+                if m.name and m.prefix != '+':
+                    m.section_name()
+    def subs(self,text,prefix='',callouts=False):
+        # If callouts is True then only callout macros are processed, if False
+        # then all non-callout macros are processed.
+        result = text
+        for m in self.macros:
+            if m.prefix == prefix:
+                if callouts ^ (m.name != 'callout'):
+                    result = m.subs(result)
+        return result
+    def isnext(self):
+        """Return matching macro if block macro is next on reader."""
+        reader.skip_blank_lines()
+        line = reader.read_next()
+        if line:
+            for m in self.macros:
+                if m.prefix == '#':
+                    if m.reo.match(line):
+                        self.current = m
+                        return m
+        return False
+    def match(self,prefix,name,text):
+        """Return re match object matching 'text' with macro type 'prefix',
+        macro name 'name'."""
+        for m in self.macros:
+            if m.prefix == prefix:
+                mo = m.reo.match(text)
+                if mo:
+                    if m.name == name:
+                        return mo
+                    if re.match(name,mo.group('name')):
+                        return mo
+        return None
+    def extract_passthroughs(self,text,prefix=''):
+        """ Extract the passthrough text and replace with temporary
+        placeholders."""
+        self.passthroughs = []
+        for m in self.macros:
+            if m.has_passthrough() and m.prefix == prefix:
+                text = m.subs_passthroughs(text, self.passthroughs)
+        return text
+    def restore_passthroughs(self,text):
+        """ Replace passthough placeholders with the original passthrough
+        text."""
+        for i,v in enumerate(self.passthroughs):
+            text = text.replace('\x07'+str(i)+'\x07', self.passthroughs[i])
+        return text
+
+class Macro:
+    def __init__(self):
+        self.pattern = None     # Matching regular expression.
+        self.name = ''          # Conf file macro name (None if implicit).
+        self.prefix = ''        # '' if inline, '+' if system, '#' if block.
+        self.reo = None         # Compiled pattern re object.
+        self.subslist = []      # Default subs for macros passtext group.
+    def has_passthrough(self):
+        return self.pattern.find(r'(?P<passtext>') >= 0
+    def section_name(self,name=None):
+        """Return macro markup template section name based on macro name and
+        prefix.  Return None section not found."""
+        assert self.prefix != '+'
+        if not name:
+            assert self.name
+            name = self.name
+        if self.prefix == '#':
+            suffix = '-blockmacro'
+        else:
+            suffix = '-inlinemacro'
+        if name+suffix in config.sections:
+            return name+suffix
+        else:
+            message.warning('missing macro section: [%s]' % (name+suffix))
+            return None
+    def load(self,entry):
+        e = parse_entry(entry)
+        if e is None:
+            # Only the macro pattern was specified, mark for deletion.
+            self.name = None
+            self.pattern = entry
+            return
+        if not is_re(e[0]):
+            raise EAsciiDoc,'illegal macro regular expression: %s' % e[0]
+        pattern, name = e
+        if name and name[0] in ('+','#'):
+            prefix, name = name[0], name[1:]
+        else:
+            prefix = ''
+        # Parse passthrough subslist.
+        mo = re.match(r'^(?P<name>[^[]*)(\[(?P<subslist>.*)\])?$', name)
+        name = mo.group('name')
+        if name and not is_name(name):
+            raise EAsciiDoc,'illegal section name in macro entry: %s' % entry
+        subslist = mo.group('subslist')
+        if subslist is not None:
+            # Parse and validate passthrough subs.
+            subslist = parse_options(subslist, SUBS_OPTIONS,
+                                 'illegal subs in macro entry: %s' % entry)
+        self.pattern = pattern
+        self.reo = re.compile(pattern)
+        self.prefix = prefix
+        self.name = name
+        self.subslist = subslist or []
+
+    def subs(self,text):
+        def subs_func(mo):
+            """Function called to perform macro substitution.
+            Uses matched macro regular expression object and returns string
+            containing the substituted macro body."""
+            # Check if macro reference is escaped.
+            if mo.group()[0] == '\\':
+                return mo.group()[1:]   # Strip leading backslash.
+            d = mo.groupdict()
+            # Delete groups that didn't participate in match.
+            for k,v in d.items():
+                if v is None: del d[k]
+            if self.name:
+                name = self.name
+            else:
+                if not 'name' in d:
+                    message.warning('missing macro name group: %s' % mo.re.pattern)
+                    return ''
+                name = d['name']
+            section_name = self.section_name(name)
+            if not section_name:
+                return ''
+            # If we're dealing with a block macro get optional block ID and
+            # block title.
+            if self.prefix == '#' and self.name != 'comment':
+                AttributeList.consume(d)
+                BlockTitle.consume(d)
+            # Parse macro attributes.
+            if 'attrlist' in d:
+                if d['attrlist'] in (None,''):
+                    del d['attrlist']
+                else:
+                    if self.prefix == '':
+                        # Unescape ] characters in inline macros.
+                        d['attrlist'] = d['attrlist'].replace('\\]',']')
+                    parse_attributes(d['attrlist'],d)
+                    # Generate option attributes.
+                    if 'options' in d:
+                        options = parse_options(d['options'], (),
+                                '%s: illegal option name' % name)
+                        for option in options:
+                            d[option+'-option'] = ''
+                    # Substitute single quoted attribute values in block macros.
+                    if self.prefix == '#':
+                        AttributeList.subs(d)
+            if name == 'callout':
+                listindex =int(d['index'])
+                d['coid'] = calloutmap.add(listindex)
+            # The alt attribute is the first image macro positional attribute.
+            if name == 'image' and '1' in d:
+                d['alt'] = d['1']
+            # Unescape special characters in LaTeX target file names.
+            if document.backend == 'latex' and 'target' in d and d['target']:
+                if not '0' in d:
+                    d['0'] = d['target']
+                d['target']= config.subs_specialchars_reverse(d['target'])
+            # BUG: We've already done attribute substitution on the macro which
+            # means that any escaped attribute references are now unescaped and
+            # will be substituted by config.subs_section() below. As a partial
+            # fix have withheld {0} from substitution but this kludge doesn't
+            # fix it for other attributes containing unescaped references.
+            # Passthrough macros don't have this problem.
+            a0 = d.get('0')
+            if a0:
+                d['0'] = chr(0)  # Replace temporarily with unused character.
+            body = config.subs_section(section_name,d)
+            if len(body) == 0:
+                result = ''
+            elif len(body) == 1:
+                result = body[0]
+            else:
+                if self.prefix == '#':
+                    result = writer.newline.join(body)
+                else:
+                    # Internally processed inline macros use UNIX line
+                    # separator.
+                    result = '\n'.join(body)
+            if a0:
+                result = result.replace(chr(0), a0)
+            return result
+
+        return self.reo.sub(subs_func, text)
+
+    def translate(self):
+        """ Block macro translation."""
+        assert self.prefix == '#'
+        s = reader.read()
+        before = s
+        if self.has_passthrough():
+            s = macros.extract_passthroughs(s,'#')
+        s = subs_attrs(s)
+        if s:
+            s = self.subs(s)
+            if self.has_passthrough():
+                s = macros.restore_passthroughs(s)
+            if s:
+                trace('macro block',before,s)
+                writer.write(s)
+
+    def subs_passthroughs(self, text, passthroughs):
+        """ Replace macro attribute lists in text with placeholders.
+        Substitute and append the passthrough attribute lists to the
+        passthroughs list."""
+        def subs_func(mo):
+            """Function called to perform inline macro substitution.
+            Uses matched macro regular expression object and returns string
+            containing the substituted macro body."""
+            # Don't process escaped macro references.
+            if mo.group()[0] == '\\':
+                return mo.group()
+            d = mo.groupdict()
+            if not 'passtext' in d:
+                message.warning('passthrough macro %s: missing passtext group' %
+                        d.get('name',''))
+                return mo.group()
+            passtext = d['passtext']
+            if re.search('\x07\\d+\x07', passtext):
+                message.warning('nested inline passthrough')
+                return mo.group()
+            if d.get('subslist'):
+                if d['subslist'].startswith(':'):
+                    message.error('block macro cannot occur here: %s' % mo.group(),
+                          halt=True)
+                subslist = parse_options(d['subslist'], SUBS_OPTIONS,
+                          'illegal passthrough macro subs option')
+            else:
+                subslist = self.subslist
+            passtext = Lex.subs_1(passtext,subslist)
+            if passtext is None: passtext = ''
+            if self.prefix == '':
+                # Unescape ] characters in inline macros.
+                passtext = passtext.replace('\\]',']')
+            passthroughs.append(passtext)
+            # Tabs guarantee the placeholders are unambiguous.
+            result = (
+                text[mo.start():mo.start('passtext')] +
+                '\x07' + str(len(passthroughs)-1) + '\x07' +
+                text[mo.end('passtext'):mo.end()]
+            )
+            return result
+
+        return self.reo.sub(subs_func, text)
+
+
+class CalloutMap:
+    def __init__(self):
+        self.comap = {}         # key = list index, value = callouts list.
+        self.calloutindex = 0   # Current callout index number.
+        self.listnumber = 1     # Current callout list number.
+    def listclose(self):
+        # Called when callout list is closed.
+        self.listnumber += 1
+        self.calloutindex = 0
+        self.comap = {}
+    def add(self,listindex):
+        # Add next callout index to listindex map entry. Return the callout id.
+        self.calloutindex += 1
+        # Append the coindex to a list in the comap dictionary.
+        if not listindex in self.comap:
+            self.comap[listindex] = [self.calloutindex]
+        else:
+            self.comap[listindex].append(self.calloutindex)
+        return self.calloutid(self.listnumber, self.calloutindex)
+    @staticmethod
+    def calloutid(listnumber,calloutindex):
+        return 'CO%d-%d' % (listnumber,calloutindex)
+    def calloutids(self,listindex):
+        # Retieve list of callout indexes that refer to listindex.
+        if listindex in self.comap:
+            result = ''
+            for coindex in self.comap[listindex]:
+                result += ' ' + self.calloutid(self.listnumber,coindex)
+            return result.strip()
+        else:
+            message.warning('no callouts refer to list item '+str(listindex))
+            return ''
+    def validate(self,maxlistindex):
+        # Check that all list indexes referenced by callouts exist.
+        for listindex in self.comap.keys():
+            if listindex > maxlistindex:
+                message.warning('callout refers to non-existent list item '
+                        + str(listindex))
+
+#---------------------------------------------------------------------------
+# Input stream Reader and output stream writer classes.
+#---------------------------------------------------------------------------
+
+UTF8_BOM = '\xef\xbb\xbf'
+
+class Reader1:
+    """Line oriented AsciiDoc input file reader. Processes include and
+    conditional inclusion system macros. Tabs are expanded and lines are right
+    trimmed."""
+    # This class is not used directly, use Reader class instead.
+    READ_BUFFER_MIN = 10        # Read buffer low level.
+    def __init__(self):
+        self.f = None           # Input file object.
+        self.fname = None       # Input file name.
+        self.next = []          # Read ahead buffer containing
+                                # [filename,linenumber,linetext] lists.
+        self.cursor = None      # Last read() [filename,linenumber,linetext].
+        self.tabsize = 8        # Tab expansion number of spaces.
+        self.parent = None      # Included reader's parent reader.
+        self._lineno = 0        # The last line read from file object f.
+        self.current_depth = 0  # Current include depth.
+        self.max_depth = 5      # Initial maxiumum allowed include depth.
+        self.bom = None         # Byte order mark (BOM).
+        self.infile = None      # Saved document 'infile' attribute.
+        self.indir = None       # Saved document 'indir' attribute.
+    def open(self,fname):
+        self.fname = fname
+        message.verbose('reading: '+fname)
+        if fname == '<stdin>':
+            self.f = sys.stdin
+            self.infile = None
+            self.indir = None
+        else:
+            self.f = open(fname,'rb')
+            self.infile = fname
+            self.indir = os.path.dirname(fname)
+        document.attributes['infile'] = self.infile
+        document.attributes['indir'] = self.indir
+        self._lineno = 0            # The last line read from file object f.
+        self.next = []
+        # Prefill buffer by reading the first line and then pushing it back.
+        if Reader1.read(self):
+            if self.cursor[2].startswith(UTF8_BOM):
+                self.cursor[2] = self.cursor[2][len(UTF8_BOM):]
+                self.bom = UTF8_BOM
+            self.unread(self.cursor)
+            self.cursor = None
+    def closefile(self):
+        """Used by class methods to close nested include files."""
+        self.f.close()
+        self.next = []
+    def close(self):
+        self.closefile()
+        self.__init__()
+    def read(self, skip=False):
+        """Read next line. Return None if EOF. Expand tabs. Strip trailing
+        white space. Maintain self.next read ahead buffer. If skip=True then
+        conditional exclusion is active (ifdef and ifndef macros)."""
+        # Top up buffer.
+        if len(self.next) <= self.READ_BUFFER_MIN:
+            s = self.f.readline()
+            if s:
+                self._lineno = self._lineno + 1
+            while s:
+                if self.tabsize != 0:
+                    s = s.expandtabs(self.tabsize)
+                s = s.rstrip()
+                self.next.append([self.fname,self._lineno,s])
+                if len(self.next) > self.READ_BUFFER_MIN:
+                    break
+                s = self.f.readline()
+                if s:
+                    self._lineno = self._lineno + 1
+        # Return first (oldest) buffer entry.
+        if len(self.next) > 0:
+            self.cursor = self.next[0]
+            del self.next[0]
+            result = self.cursor[2]
+            # Check for include macro.
+            mo = macros.match('+',r'include[1]?',result)
+            if mo and not skip:
+                # Don't process include macro once the maximum depth is reached.
+                if self.current_depth >= self.max_depth:
+                    return result
+                # Perform attribute substitution on include macro file name.
+                fname = subs_attrs(mo.group('target'))
+                if not fname:
+                    return Reader1.read(self)   # Return next input line.
+                if self.fname != '<stdin>':
+                    fname = os.path.expandvars(os.path.expanduser(fname))
+                    fname = safe_filename(fname, os.path.dirname(self.fname))
+                    if not fname:
+                        return Reader1.read(self)   # Return next input line.
+                    if mo.group('name') == 'include1':
+                        if not config.dumping:
+                            # Store the include file in memory for later
+                            # retrieval by the {include1:} system attribute.
+                            config.include1[fname] = [
+                                s.rstrip() for s in open(fname)]
+                            return '{include1:%s}' % fname
+                        else:
+                            # This is a configuration dump, just pass the macro
+                            # call through.
+                            return result
+                # Parse include macro attributes.
+                attrs = {}
+                parse_attributes(mo.group('attrlist'),attrs)
+                # Clone self and set as parent (self assumes the role of child).
+                parent = Reader1()
+                assign(parent,self)
+                self.parent = parent
+                # Set attributes in child.
+                if 'tabsize' in attrs:
+                    self.tabsize = int(validate(attrs['tabsize'],
+                        'int($)>=0',
+                        'illegal include macro tabsize argument'))
+                else:
+                    self.tabsize = config.tabsize
+                if 'depth' in attrs:
+                    attrs['depth'] = int(validate(attrs['depth'],
+                        'int($)>=1',
+                        'illegal include macro depth argument'))
+                    self.max_depth = self.current_depth + attrs['depth']
+                # Process included file.
+                self.open(fname)
+                self.current_depth = self.current_depth + 1
+                result = Reader1.read(self)
+        else:
+            if not Reader1.eof(self):
+                result = Reader1.read(self)
+            else:
+                result = None
+        return result
+    def eof(self):
+        """Returns True if all lines have been read."""
+        if len(self.next) == 0:
+            # End of current file.
+            if self.parent:
+                self.closefile()
+                assign(self,self.parent)    # Restore parent reader.
+                document.attributes['infile'] = self.infile
+                document.attributes['indir'] = self.indir
+                return Reader1.eof(self)
+            else:
+                return True
+        else:
+            return False
+    def read_next(self):
+        """Like read() but does not advance file pointer."""
+        if Reader1.eof(self):
+            return None
+        else:
+            return self.next[0][2]
+    def unread(self,cursor):
+        """Push the line (filename,linenumber,linetext) tuple back into the read
+        buffer. Note that it's up to the caller to restore the previous
+        cursor."""
+        assert cursor
+        self.next.insert(0,cursor)
+
+class Reader(Reader1):
+    """ Wraps (well, sought of) Reader1 class and implements conditional text
+    inclusion."""
+    def __init__(self):
+        Reader1.__init__(self)
+        self.depth = 0          # if nesting depth.
+        self.skip = False       # true if we're skipping ifdef...endif.
+        self.skipname = ''      # Name of current endif macro target.
+        self.skipto = -1        # The depth at which skipping is reenabled.
+    def read_super(self):
+        result = Reader1.read(self,self.skip)
+        if result is None and self.skip:
+            raise EAsciiDoc,'missing endif::%s[]' % self.skipname
+        return result
+    def read(self):
+        result = self.read_super()
+        if result is None:
+            return None
+        while self.skip:
+            mo = macros.match('+',r'ifdef|ifndef|ifeval|endif',result)
+            if mo:
+                name = mo.group('name')
+                target = mo.group('target')
+                attrlist = mo.group('attrlist')
+                if name == 'endif':
+                    self.depth -= 1
+                    if self.depth < 0:
+                        raise EAsciiDoc,'mismatched macro: %s' % result
+                    if self.depth == self.skipto:
+                        self.skip = False
+                        if target and self.skipname != target:
+                            raise EAsciiDoc,'mismatched macro: %s' % result
+                else:
+                    if name in ('ifdef','ifndef'):
+                        if not target:
+                            raise EAsciiDoc,'missing macro target: %s' % result
+                        if not attrlist:
+                            self.depth += 1
+                    elif name == 'ifeval':
+                        if not attrlist:
+                            raise EAsciiDoc,'missing ifeval condition: %s' % result
+                        self.depth += 1
+            result = self.read_super()
+            if result is None:
+                return None
+        mo = macros.match('+',r'ifdef|ifndef|ifeval|endif',result)
+        if mo:
+            name = mo.group('name')
+            target = mo.group('target')
+            attrlist = mo.group('attrlist')
+            if name == 'endif':
+                self.depth = self.depth-1
+            else:
+                if not target and name in ('ifdef','ifndef'):
+                    raise EAsciiDoc,'missing macro target: %s' % result
+                defined = is_attr_defined(target, document.attributes)
+                if name == 'ifdef':
+                    if attrlist:
+                        if defined: return attrlist
+                    else:
+                        self.skip = not defined
+                elif name == 'ifndef':
+                    if attrlist:
+                        if not defined: return attrlist
+                    else:
+                        self.skip = defined
+                elif name == 'ifeval':
+                    if not attrlist:
+                        raise EAsciiDoc,'missing ifeval condition: %s' % result
+                    cond = False
+                    attrlist = subs_attrs(attrlist)
+                    if attrlist:
+                        try:
+                            cond = eval(attrlist)
+                        except Exception,e:
+                            raise EAsciiDoc,'error evaluating ifeval condition: %s: %s' % (result, str(e))
+                    self.skip = not cond
+                if not attrlist or name == 'ifeval':
+                    if self.skip:
+                        self.skipto = self.depth
+                        self.skipname = target
+                    self.depth = self.depth+1
+            result = self.read()
+        if result:
+            # Expand executable block macros.
+            mo = macros.match('+',r'eval|sys|sys2',result)
+            if mo:
+                action = mo.group('name')
+                cmd = mo.group('attrlist')
+                s = system(action, cmd, is_macro=True)
+                if s is not None:
+                    self.cursor[2] = s  # So we don't re-evaluate.
+                    result = s
+        if result:
+            # Unescape escaped system macros.
+            if macros.match('+',r'\\eval|\\sys|\\sys2|\\ifdef|\\ifndef|\\endif|\\include|\\include1',result):
+                result = result[1:]
+        return result
+    def eof(self):
+        return self.read_next() is None
+    def read_next(self):
+        save_cursor = self.cursor
+        result = self.read()
+        if result is not None:
+            self.unread(self.cursor)
+            self.cursor = save_cursor
+        return result
+    def read_lines(self,count=1):
+        """Return tuple containing count lines."""
+        result = []
+        i = 0
+        while i < count and not self.eof():
+            result.append(self.read())
+        return tuple(result)
+    def read_ahead(self,count=1):
+        """Same as read_lines() but does not advance the file pointer."""
+        result = []
+        putback = []
+        save_cursor = self.cursor
+        try:
+            i = 0
+            while i < count and not self.eof():
+                result.append(self.read())
+                putback.append(self.cursor)
+                i = i+1
+            while putback:
+                self.unread(putback.pop())
+        finally:
+            self.cursor = save_cursor
+        return tuple(result)
+    def skip_blank_lines(self):
+        reader.read_until(r'\s*\S+')
+    def read_until(self,terminators,same_file=False):
+        """Like read() but reads lines up to (but not including) the first line
+        that matches the terminator regular expression, regular expression
+        object or list of regular expression objects. If same_file is True then
+        the terminating pattern must occur in the file the was being read when
+        the routine was called."""
+        if same_file:
+            fname = self.cursor[0]
+        result = []
+        if not isinstance(terminators,list):
+            if isinstance(terminators,basestring):
+                terminators = [re.compile(terminators)]
+            else:
+                terminators = [terminators]
+        while not self.eof():
+            save_cursor = self.cursor
+            s = self.read()
+            if not same_file or fname == self.cursor[0]:
+                for reo in terminators:
+                    if reo.match(s):
+                        self.unread(self.cursor)
+                        self.cursor = save_cursor
+                        return tuple(result)
+            result.append(s)
+        return tuple(result)
+
+class Writer:
+    """Writes lines to output file."""
+    def __init__(self):
+        self.newline = '\r\n'            # End of line terminator.
+        self.f = None                    # Output file object.
+        self.fname = None                # Output file name.
+        self.lines_out = 0               # Number of lines written.
+        self.skip_blank_lines = False    # If True don't output blank lines.
+    def open(self,fname,bom=None):
+        '''
+        bom is optional byte order mark.
+        http://en.wikipedia.org/wiki/Byte-order_mark
+        '''
+        self.fname = fname
+        if fname == '<stdout>':
+            self.f = sys.stdout
+        else:
+            self.f = open(fname,'wb+')
+        message.verbose('writing: '+writer.fname,False)
+        if bom:
+            self.f.write(bom)
+        self.lines_out = 0
+    def close(self):
+        if self.fname != '<stdout>':
+            self.f.close()
+    def write_line(self, line=None):
+        if not (self.skip_blank_lines and (not line or not line.strip())):
+            self.f.write((line or '') + self.newline)
+            self.lines_out = self.lines_out + 1
+    def write(self,*args,**kwargs):
+        """Iterates arguments, writes tuple and list arguments one line per
+        element, else writes argument as single line. If no arguments writes
+        blank line. If argument is None nothing is written. self.newline is
+        appended to each line."""
+        if 'trace' in kwargs and len(args) > 0:
+            trace(kwargs['trace'],args[0])
+        if len(args) == 0:
+            self.write_line()
+            self.lines_out = self.lines_out + 1
+        else:
+            for arg in args:
+                if is_array(arg):
+                    for s in arg:
+                        self.write_line(s)
+                elif arg is not None:
+                    self.write_line(arg)
+    def write_tag(self,tag,content,subs=None,d=None,**kwargs):
+        """Write content enveloped by tag.
+        Substitutions specified in the 'subs' list are perform on the
+        'content'."""
+        if subs is None:
+            subs = config.subsnormal
+        stag,etag = subs_tag(tag,d)
+        content = Lex.subs(content,subs)
+        if 'trace' in kwargs:
+            trace(kwargs['trace'],[stag]+content+[etag])
+        if stag:
+            self.write(stag)
+        if content:
+            self.write(content)
+        if etag:
+            self.write(etag)
+
+#---------------------------------------------------------------------------
+# Configuration file processing.
+#---------------------------------------------------------------------------
+def _subs_specialwords(mo):
+    """Special word substitution function called by
+    Config.subs_specialwords()."""
+    word = mo.re.pattern                    # The special word.
+    template = config.specialwords[word]    # The corresponding markup template.
+    if not template in config.sections:
+        raise EAsciiDoc,'missing special word template [%s]' % template
+    if mo.group()[0] == '\\':
+        return mo.group()[1:]   # Return escaped word.
+    args = {}
+    args['words'] = mo.group()  # The full match string is argument 'words'.
+    args.update(mo.groupdict()) # Add other named match groups to the arguments.
+    # Delete groups that didn't participate in match.
+    for k,v in args.items():
+        if v is None: del args[k]
+    lines = subs_attrs(config.sections[template],args)
+    if len(lines) == 0:
+        result = ''
+    elif len(lines) == 1:
+        result = lines[0]
+    else:
+        result = writer.newline.join(lines)
+    return result
+
+class Config:
+    """Methods to process configuration files."""
+    # Non-template section name regexp's.
+    ENTRIES_SECTIONS= ('tags','miscellaneous','attributes','specialcharacters',
+            'specialwords','macros','replacements','quotes','titles',
+            r'paradef-.+',r'listdef-.+',r'blockdef-.+',r'tabledef-.+',
+            r'tabletags-.+',r'listtags-.+','replacements2',
+            r'old_tabledef-.+')
+    def __init__(self):
+        self.sections = OrderedDict()   # Keyed by section name containing
+                                        # lists of section lines.
+        # Command-line options.
+        self.verbose = False
+        self.header_footer = True       # -s, --no-header-footer option.
+        # [miscellaneous] section.
+        self.tabsize = 8
+        self.textwidth = 70             # DEPRECATED: Old tables only.
+        self.newline = '\r\n'
+        self.pagewidth = None
+        self.pageunits = None
+        self.outfilesuffix = ''
+        self.subsnormal = SUBS_NORMAL
+        self.subsverbatim = SUBS_VERBATIM
+
+        self.tags = {}          # Values contain (stag,etag) tuples.
+        self.specialchars = {}  # Values of special character substitutions.
+        self.specialwords = {}  # Name is special word pattern, value is macro.
+        self.replacements = OrderedDict()   # Key is find pattern, value is
+                                            #replace pattern.
+        self.replacements2 = OrderedDict()
+        self.specialsections = {} # Name is special section name pattern, value
+                                  # is corresponding section name.
+        self.quotes = OrderedDict()    # Values contain corresponding tag name.
+        self.fname = ''         # Most recently loaded configuration file name.
+        self.conf_attrs = {}    # Attributes entries from conf files.
+        self.cmd_attrs = {}     # Attributes from command-line -a options.
+        self.loaded = []        # Loaded conf files.
+        self.include1 = {}      # Holds include1::[] files for {include1:}.
+        self.dumping = False    # True if asciidoc -c option specified.
+
+    def init(self, cmd):
+        """
+        Check Python version and locate the executable and configuration files
+        directory.
+        cmd is the asciidoc command or asciidoc.py path.
+        """
+        if float(sys.version[:3]) < MIN_PYTHON_VERSION:
+            message.stderr('FAILED: Python 2.3 or better required')
+            sys.exit(1)
+        if not os.path.exists(cmd):
+            message.stderr('FAILED: Missing asciidoc command: %s' % cmd)
+            sys.exit(1)
+        global APP_FILE
+        APP_FILE = os.path.realpath(cmd)
+        global APP_DIR
+        APP_DIR = os.path.dirname(APP_FILE)
+        global USER_DIR
+        USER_DIR = userdir()
+        if USER_DIR is not None:
+            USER_DIR = os.path.join(USER_DIR,'.asciidoc')
+            if not os.path.isdir(USER_DIR):
+                USER_DIR = None
+
+    def load_file(self, fname, dir=None, include=[], exclude=[]):
+        """
+        Loads sections dictionary with sections from file fname.
+        Existing sections are overlaid.
+        The 'include' list contains the section names to be loaded.
+        The 'exclude' list contains section names not to be loaded.
+        Return False if no file was found in any of the locations.
+        """
+        if dir:
+            fname = os.path.join(dir, fname)
+        # Sliently skip missing configuration file.
+        if not os.path.isfile(fname):
+            return False
+        # Don't load conf files twice (local and application conf files are the
+        # same if the source file is in the application directory).
+        if os.path.realpath(fname) in self.loaded:
+            return True
+        rdr = Reader()  # Reader processes system macros.
+        message.linenos = False         # Disable document line numbers.
+        rdr.open(fname)
+        message.linenos = None
+        self.fname = fname
+        reo = re.compile(r'(?u)^\[(?P<section>[^\W\d][\w-]*)\]\s*$')
+        sections = OrderedDict()
+        section,contents = '',[]
+        while not rdr.eof():
+            s = rdr.read()
+            if s and s[0] == '#':       # Skip comment lines.
+                continue
+            if s[:2] == '\\#':          # Unescape lines starting with '#'.
+                s = s[1:]
+            s = s.rstrip()
+            found = reo.findall(s)
+            if found:
+                if section:             # Store previous section.
+                    if section in sections \
+                        and self.entries_section(section):
+                        if ''.join(contents):
+                            # Merge entries.
+                            sections[section] = sections[section] + contents
+                        else:
+                            del sections[section]
+                    else:
+                        sections[section] = contents
+                section = found[0].lower()
+                contents = []
+            else:
+                contents.append(s)
+        if section and contents:        # Store last section.
+            if section in sections \
+                and self.entries_section(section):
+                if ''.join(contents):
+                    # Merge entries.
+                    sections[section] = sections[section] + contents
+                else:
+                    del sections[section]
+            else:
+                sections[section] = contents
+        rdr.close()
+        if include:
+            for s in set(sections) - set(include):
+                del sections[s]
+        if exclude:
+            for s in set(sections) & set(exclude):
+                del sections[s]
+        attrs = {}
+        self.load_sections(sections,attrs)
+        if not include:
+            # If all sections are loaded mark this file as loaded.
+            self.loaded.append(os.path.realpath(fname))
+        document.update_attributes(attrs) # So they are available immediately.
+        return True
+
+    def load_sections(self,sections,attrs=None):
+        """
+        Loads sections dictionary. Each dictionary entry contains a
+        list of lines.
+        Updates 'attrs' with parsed [attributes] section entries.
+        """
+        # Delete trailing blank lines from sections.
+        for k in sections.keys():
+            for i in range(len(sections[k])-1,-1,-1):
+                if not sections[k][i]:
+                    del sections[k][i]
+                elif not self.entries_section(k):
+                    break
+        # Add/overwrite new sections.
+        self.sections.update(sections)
+        self.parse_tags()
+        # Internally [miscellaneous] section entries are just attributes.
+        d = {}
+        parse_entries(sections.get('miscellaneous',()), d, unquote=True,
+                allow_name_only=True)
+        parse_entries(sections.get('attributes',()), d, unquote=True,
+                allow_name_only=True)
+        update_attrs(self.conf_attrs,d)
+        if attrs is not None:
+            attrs.update(d)
+        d = {}
+        parse_entries(sections.get('titles',()),d)
+        Title.load(d)
+        parse_entries(sections.get('specialcharacters',()),self.specialchars,escape_delimiter=False)
+        parse_entries(sections.get('quotes',()),self.quotes)
+        self.parse_specialwords()
+        self.parse_replacements()
+        self.parse_replacements('replacements2')
+        self.parse_specialsections()
+        paragraphs.load(sections)
+        lists.load(sections)
+        blocks.load(sections)
+        tables_OLD.load(sections)
+        tables.load(sections)
+        macros.load(sections.get('macros',()))
+
+    def get_load_dirs(self):
+        """
+        Return list of well known paths with conf files.
+        """
+        result = []
+        if localapp():
+            # Load from folders in asciidoc executable directory.
+            result.append(APP_DIR)
+        else:
+            # Load from global configuration directory.
+            result.append(CONF_DIR)
+        # Load configuration files from ~/.asciidoc if it exists.
+        if USER_DIR is not None:
+            result.append(USER_DIR)
+        return result
+
+    def find_in_dirs(self, filename, dirs=None):
+        """
+        Find conf files from dirs list.
+        Return list of found file paths.
+        Return empty list if not found in any of the locations.
+        """
+        result = []
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            f = os.path.join(d,filename)
+            if os.path.isfile(f):
+                result.append(f)
+        return result
+
+    def load_from_dirs(self, filename, dirs=None, include=[]):
+        """
+        Load conf file from dirs list.
+        If dirs not specified try all the well known locations.
+        Return False if no file was sucessfully loaded.
+        """
+        count = 0
+        for f in self.find_in_dirs(filename,dirs):
+            if self.load_file(f, include=include):
+                count += 1
+        return count != 0
+
+    def load_backend(self, dirs=None):
+        """
+        Load the backend configuration files from dirs list.
+        If dirs not specified try all the well known locations.
+        """
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            conf = document.backend + '.conf'
+            self.load_file(conf,d)
+            conf = document.backend + '-' + document.doctype + '.conf'
+            self.load_file(conf,d)
+
+    def load_filters(self, dirs=None):
+        """
+        Load filter configuration files from 'filters' directory in dirs list.
+        If dirs not specified try all the well known locations.
+        """
+        if dirs is None:
+            dirs = self.get_load_dirs()
+        for d in dirs:
+            # Load filter .conf files.
+            filtersdir = os.path.join(d,'filters')
+            for dirpath,dirnames,filenames in os.walk(filtersdir):
+                for f in filenames:
+                    if re.match(r'^.+\.conf$',f):
+                        self.load_file(f,dirpath)
+
+    def load_miscellaneous(self,d):
+        """Set miscellaneous configuration entries from dictionary 'd'."""
+        def set_misc(name,rule='True',intval=False):
+            if name in d:
+                errmsg = 'illegal [miscellaneous] %s entry' % name
+                if intval:
+                    setattr(self, name, int(validate(d[name],rule,errmsg)))
+                else:
+                    setattr(self, name, validate(d[name],rule,errmsg))
+        set_misc('tabsize','int($)>0',intval=True)
+        set_misc('textwidth','int($)>0',intval=True) # DEPRECATED: Old tables only.
+        set_misc('pagewidth','"%f" % $')
+        if 'pagewidth' in d:
+            self.pagewidth = float(self.pagewidth)
+        set_misc('pageunits')
+        set_misc('outfilesuffix')
+        if 'newline' in d:
+            # Convert escape sequences to their character values.
+            self.newline = eval('"'+d['newline']+'"')
+        if 'subsnormal' in d:
+            self.subsnormal = parse_options(d['subsnormal'],SUBS_OPTIONS,
+                    'illegal [%s] %s: %s' %
+                    ('miscellaneous','subsnormal',d['subsnormal']))
+        if 'subsverbatim' in d:
+            self.subsverbatim = parse_options(d['subsverbatim'],SUBS_OPTIONS,
+                    'illegal [%s] %s: %s' %
+                    ('miscellaneous','subsverbatim',d['subsverbatim']))
+
+    def validate(self):
+        """Check the configuration for internal consistancy. Called after all
+        configuration files have been loaded."""
+        message.linenos = False     # Disable document line numbers.
+        # Heuristic to validate that at least one configuration file was loaded.
+        if not self.specialchars or not self.tags or not lists:
+            raise EAsciiDoc,'incomplete configuration files'
+        # Check special characters are only one character long.
+        for k in self.specialchars.keys():
+            if len(k) != 1:
+                raise EAsciiDoc,'[specialcharacters] ' \
+                                'must be a single character: %s' % k
+        # Check all special words have a corresponding inline macro body.
+        for macro in self.specialwords.values():
+            if not is_name(macro):
+                raise EAsciiDoc,'illegal special word name: %s' % macro
+            if not macro in self.sections:
+                message.warning('missing special word macro: [%s]' % macro)
+        # Check all text quotes have a corresponding tag.
+        for q in self.quotes.keys()[:]:
+            tag = self.quotes[q]
+            if not tag:
+                del self.quotes[q]  # Undefine quote.
+            else:
+                if tag[0] == '#':
+                    tag = tag[1:]
+                if not tag in self.tags:
+                    message.warning('[quotes] %s missing tag definition: %s' % (q,tag))
+        # Check all specialsections section names exist.
+        for k,v in self.specialsections.items():
+            if not v:
+                del self.specialsections[k]
+            elif not v in self.sections:
+                message.warning('missing specialsections section: [%s]' % v)
+        paragraphs.validate()
+        lists.validate()
+        blocks.validate()
+        tables_OLD.validate()
+        tables.validate()
+        macros.validate()
+        message.linenos = None
+
+    def entries_section(self,section_name):
+        """
+        Return True if conf file section contains entries, not a markup
+        template.
+        """
+        for name in self.ENTRIES_SECTIONS:
+            if re.match(name,section_name):
+                return True
+        return False
+
+    def dump(self):
+        """Dump configuration to stdout."""
+        # Header.
+        hdr = ''
+        hdr = hdr + '#' + writer.newline
+        hdr = hdr + '# Generated by AsciiDoc %s for %s %s.%s' % \
+            (VERSION,document.backend,document.doctype,writer.newline)
+        t = time.asctime(time.localtime(time.time()))
+        hdr = hdr + '# %s%s' % (t,writer.newline)
+        hdr = hdr + '#' + writer.newline
+        sys.stdout.write(hdr)
+        # Dump special sections.
+        # Dump only the configuration file and command-line attributes.
+        # [miscellanous] entries are dumped as part of the [attributes].
+        d = {}
+        d.update(self.conf_attrs)
+        d.update(self.cmd_attrs)
+        dump_section('attributes',d)
+        Title.dump()
+        dump_section('quotes',self.quotes)
+        dump_section('specialcharacters',self.specialchars)
+        d = {}
+        for k,v in self.specialwords.items():
+            if v in d:
+                d[v] = '%s "%s"' % (d[v],k)   # Append word list.
+            else:
+                d[v] = '"%s"' % k
+        dump_section('specialwords',d)
+        dump_section('replacements',self.replacements)
+        dump_section('replacements2',self.replacements2)
+        dump_section('specialsections',self.specialsections)
+        d = {}
+        for k,v in self.tags.items():
+            d[k] = '%s|%s' % v
+        dump_section('tags',d)
+        paragraphs.dump()
+        lists.dump()
+        blocks.dump()
+        tables_OLD.dump()
+        tables.dump()
+        macros.dump()
+        # Dump remaining sections.
+        for k in self.sections.keys():
+            if not self.entries_section(k):
+                sys.stdout.write('[%s]%s' % (k,writer.newline))
+                for line in self.sections[k]:
+                    sys.stdout.write('%s%s' % (line,writer.newline))
+                sys.stdout.write(writer.newline)
+
+    def subs_section(self,section,d):
+        """Section attribute substitution using attributes from
+        document.attributes and 'd'.  Lines containing undefinded
+        attributes are deleted."""
+        if section in self.sections:
+            return subs_attrs(self.sections[section],d)
+        else:
+            message.warning('missing section: [%s]' % section)
+            return ()
+
+    def parse_tags(self):
+        """Parse [tags] section entries into self.tags dictionary."""
+        d = {}
+        parse_entries(self.sections.get('tags',()),d)
+        for k,v in d.items():
+            if v is None:
+                if k in self.tags:
+                    del self.tags[k]
+            elif v == '':
+                self.tags[k] = (None,None)
+            else:
+                mo = re.match(r'(?P<stag>.*)\|(?P<etag>.*)',v)
+                if mo:
+                    self.tags[k] = (mo.group('stag'), mo.group('etag'))
+                else:
+                    raise EAsciiDoc,'[tag] %s value malformed' % k
+
+    def tag(self, name, d=None):
+        """Returns (starttag,endtag) tuple named name from configuration file
+        [tags] section. Raise error if not found. If a dictionary 'd' is
+        passed then merge with document attributes and perform attribute
+        substitution on tags."""
+        if not name in self.tags:
+            raise EAsciiDoc, 'missing tag: %s' % name
+        stag,etag = self.tags[name]
+        if d is not None:
+            # TODO: Should we warn if substitution drops a tag?
+            if stag:
+                stag = subs_attrs(stag,d)
+            if etag:
+                etag = subs_attrs(etag,d)
+        if stag is None: stag = ''
+        if etag is None: etag = ''
+        return (stag,etag)
+
+    def parse_specialsections(self):
+        """Parse specialsections section to self.specialsections dictionary."""
+        # TODO: This is virtually the same as parse_replacements() and should
+        # be factored to single routine.
+        d = {}
+        parse_entries(self.sections.get('specialsections',()),d,unquote=True)
+        for pat,sectname in d.items():
+            pat = strip_quotes(pat)
+            if not is_re(pat):
+                raise EAsciiDoc,'[specialsections] entry ' \
+                                'is not a valid regular expression: %s' % pat
+            if sectname is None:
+                if pat in self.specialsections:
+                    del self.specialsections[pat]
+            else:
+                self.specialsections[pat] = sectname
+
+    def parse_replacements(self,sect='replacements'):
+        """Parse replacements section into self.replacements dictionary."""
+        d = OrderedDict()
+        parse_entries(self.sections.get(sect,()), d, unquote=True)
+        for pat,rep in d.items():
+            if not self.set_replacement(pat, rep, getattr(self,sect)):
+                raise EAsciiDoc,'[%s] entry in %s is not a valid' \
+                    ' regular expression: %s' % (sect,self.fname,pat)
+
+    @staticmethod
+    def set_replacement(pat, rep, replacements):
+        """Add pattern and replacement to replacements dictionary."""
+        pat = strip_quotes(pat)
+        if not is_re(pat):
+            return False
+        if rep is None:
+            if pat in replacements:
+                del replacements[pat]
+        else:
+            replacements[pat] = strip_quotes(rep)
+        return True
+
+    def subs_replacements(self,s,sect='replacements'):
+        """Substitute patterns from self.replacements in 's'."""
+        result = s
+        for pat,rep in getattr(self,sect).items():
+            result = re.sub(pat, rep, result)
+        return result
+
+    def parse_specialwords(self):
+        """Parse special words section into self.specialwords dictionary."""
+        reo = re.compile(r'(?:\s|^)(".+?"|[^"\s]+)(?=\s|$)')
+        for line in self.sections.get('specialwords',()):
+            e = parse_entry(line)
+            if not e:
+                raise EAsciiDoc,'[specialwords] entry in %s is malformed: %s' \
+                    % (self.fname,line)
+            name,wordlist = e
+            if not is_name(name):
+                raise EAsciiDoc,'[specialwords] name in %s is illegal: %s' \
+                    % (self.fname,name)
+            if wordlist is None:
+                # Undefine all words associated with 'name'.
+                for k,v in self.specialwords.items():
+                    if v == name:
+                        del self.specialwords[k]
+            else:
+                words = reo.findall(wordlist)
+                for word in words:
+                    word = strip_quotes(word)
+                    if not is_re(word):
+                        raise EAsciiDoc,'[specialwords] entry in %s ' \
+                            'is not a valid regular expression: %s' \
+                            % (self.fname,word)
+                    self.specialwords[word] = name
+
+    def subs_specialchars(self,s):
+        """Perform special character substitution on string 's'."""
+        """It may seem like a good idea to escape special characters with a '\'
+        character, the reason we don't is because the escape character itself
+        then has to be escaped and this makes including code listings
+        problematic. Use the predefined {amp},{lt},{gt} attributes instead."""
+        result = ''
+        for ch in s:
+            result = result + self.specialchars.get(ch,ch)
+        return result
+
+    def subs_specialchars_reverse(self,s):
+        """Perform reverse special character substitution on string 's'."""
+        result = s
+        for k,v in self.specialchars.items():
+            result = result.replace(v, k)
+        return result
+
+    def subs_specialwords(self,s):
+        """Search for word patterns from self.specialwords in 's' and
+        substitute using corresponding macro."""
+        result = s
+        for word in self.specialwords.keys():
+            result = re.sub(word, _subs_specialwords, result)
+        return result
+
+    def expand_templates(self,entries):
+        """Expand any template::[] macros in a list of section entries."""
+        result = []
+        for line in entries:
+            mo = macros.match('+',r'template',line)
+            if mo:
+                s = mo.group('attrlist')
+                if s in self.sections:
+                    result += self.expand_templates(self.sections[s])
+                else:
+                    message.warning('missing section: [%s]' % s)
+                    result.append(line)
+            else:
+                result.append(line)
+        return result
+
+    def expand_all_templates(self):
+        for k,v in self.sections.items():
+            self.sections[k] = self.expand_templates(v)
+
+    def section2tags(self, section, d={}, skipstart=False, skipend=False):
+        """Perform attribute substitution on 'section' using document
+        attributes plus 'd' attributes. Return tuple (stag,etag) containing
+        pre and post | placeholder tags. 'skipstart' and 'skipend' are
+        used to suppress substitution."""
+        assert section is not None
+        if section in self.sections:
+            body = self.sections[section]
+        else:
+            message.warning('missing section: [%s]' % section)
+            body = ()
+        # Split macro body into start and end tag lists.
+        stag = []
+        etag = []
+        in_stag = True
+        for s in body:
+            if in_stag:
+                mo = re.match(r'(?P<stag>.*)\|(?P<etag>.*)',s)
+                if mo:
+                    if mo.group('stag'):
+                        stag.append(mo.group('stag'))
+                    if mo.group('etag'):
+                        etag.append(mo.group('etag'))
+                    in_stag = False
+                else:
+                    stag.append(s)
+            else:
+                etag.append(s)
+        # Do attribute substitution last so {brkbar} can be used to escape |.
+        # But don't do attribute substitution on title -- we've already done it.
+        title = d.get('title')
+        if title:
+            d['title'] = chr(0)  # Replace with unused character.
+        if not skipstart:
+            stag = subs_attrs(stag, d)
+        if not skipend:
+            etag = subs_attrs(etag, d)
+        # Put the {title} back.
+        if title:
+            stag = map(lambda x: x.replace(chr(0), title), stag)
+            etag = map(lambda x: x.replace(chr(0), title), etag)
+            d['title'] = title
+        return (stag,etag)
+
+
+#---------------------------------------------------------------------------
+# Deprecated old table classes follow.
+# Naming convention is an _OLD name suffix.
+# These will be removed from future versions of AsciiDoc
+
+def join_lines_OLD(lines):
+    """Return a list in which lines terminated with the backslash line
+    continuation character are joined."""
+    result = []
+    s = ''
+    continuation = False
+    for line in lines:
+        if line and line[-1] == '\\':
+            s = s + line[:-1]
+            continuation = True
+            continue
+        if continuation:
+            result.append(s+line)
+            s = ''
+            continuation = False
+        else:
+            result.append(line)
+    if continuation:
+        result.append(s)
+    return result
+
+class Column_OLD:
+    """Table column."""
+    def __init__(self):
+        self.colalign = None    # 'left','right','center'
+        self.rulerwidth = None
+        self.colwidth = None    # Output width in page units.
+
+class Table_OLD(AbstractBlock):
+    COL_STOP = r"(`|'|\.)"  # RE.
+    ALIGNMENTS = {'`':'left', "'":'right', '.':'center'}
+    FORMATS = ('fixed','csv','dsv')
+    def __init__(self):
+        AbstractBlock.__init__(self)
+        self.CONF_ENTRIES += ('template','fillchar','format','colspec',
+                              'headrow','footrow','bodyrow','headdata',
+                              'footdata', 'bodydata')
+        # Configuration parameters.
+        self.fillchar=None
+        self.format=None    # 'fixed','csv','dsv'
+        self.colspec=None
+        self.headrow=None
+        self.footrow=None
+        self.bodyrow=None
+        self.headdata=None
+        self.footdata=None
+        self.bodydata=None
+        # Calculated parameters.
+        self.underline=None     # RE matching current table underline.
+        self.isnumeric=False    # True if numeric ruler.
+        self.tablewidth=None    # Optional table width scale factor.
+        self.columns=[]         # List of Columns.
+        # Other.
+        self.check_msg=''       # Message set by previous self.validate() call.
+    def load(self,name,entries):
+        AbstractBlock.load(self,name,entries)
+        """Update table definition from section entries in 'entries'."""
+        for k,v in entries.items():
+            if k == 'fillchar':
+                if v and len(v) == 1:
+                    self.fillchar = v
+                else:
+                    raise EAsciiDoc,'malformed table fillchar: %s' % v
+            elif k == 'format':
+                if v in Table_OLD.FORMATS:
+                    self.format = v
+                else:
+                    raise EAsciiDoc,'illegal table format: %s' % v
+            elif k == 'colspec':
+                self.colspec = v
+            elif k == 'headrow':
+                self.headrow = v
+            elif k == 'footrow':
+                self.footrow = v
+            elif k == 'bodyrow':
+                self.bodyrow = v
+            elif k == 'headdata':
+                self.headdata = v
+            elif k == 'footdata':
+                self.footdata = v
+            elif k == 'bodydata':
+                self.bodydata = v
+    def dump(self):
+        AbstractBlock.dump(self)
+        write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
+        write('fillchar='+self.fillchar)
+        write('format='+self.format)
+        if self.colspec:
+            write('colspec='+self.colspec)
+        if self.headrow:
+            write('headrow='+self.headrow)
+        if self.footrow:
+            write('footrow='+self.footrow)
+        write('bodyrow='+self.bodyrow)
+        if self.headdata:
+            write('headdata='+self.headdata)
+        if self.footdata:
+            write('footdata='+self.footdata)
+        write('bodydata='+self.bodydata)
+        write('')
+    def validate(self):
+        AbstractBlock.validate(self)
+        """Check table definition and set self.check_msg if invalid else set
+        self.check_msg to blank string."""
+        # Check global table parameters.
+        if config.textwidth is None:
+            self.check_msg = 'missing [miscellaneous] textwidth entry'
+        elif config.pagewidth is None:
+            self.check_msg = 'missing [miscellaneous] pagewidth entry'
+        elif config.pageunits is None:
+            self.check_msg = 'missing [miscellaneous] pageunits entry'
+        elif self.headrow is None:
+            self.check_msg = 'missing headrow entry'
+        elif self.footrow is None:
+            self.check_msg = 'missing footrow entry'
+        elif self.bodyrow is None:
+            self.check_msg = 'missing bodyrow entry'
+        elif self.headdata is None:
+            self.check_msg = 'missing headdata entry'
+        elif self.footdata is None:
+            self.check_msg = 'missing footdata entry'
+        elif self.bodydata is None:
+            self.check_msg = 'missing bodydata entry'
+        else:
+            # No errors.
+            self.check_msg = ''
+    def isnext(self):
+        return AbstractBlock.isnext(self)
+    def parse_ruler(self,ruler):
+        """Parse ruler calculating underline and ruler column widths."""
+        fc = re.escape(self.fillchar)
+        # Strip and save optional tablewidth from end of ruler.
+        mo = re.match(r'^(.*'+fc+r'+)([\d\.]+)$',ruler)
+        if mo:
+            ruler = mo.group(1)
+            self.tablewidth = float(mo.group(2))
+            self.attributes['tablewidth'] = str(float(self.tablewidth))
+        else:
+            self.tablewidth = None
+            self.attributes['tablewidth'] = '100.0'
+        # Guess whether column widths are specified numerically or not.
+        if ruler[1] != self.fillchar:
+            # If the first column does not start with a fillchar then numeric.
+            self.isnumeric = True
+        elif ruler[1:] == self.fillchar*len(ruler[1:]):
+            # The case of one column followed by fillchars is numeric.
+            self.isnumeric = True
+        else:
+            self.isnumeric = False
+        # Underlines must be 3 or more fillchars.
+        self.underline = r'^' + fc + r'{3,}$'
+        splits = re.split(self.COL_STOP,ruler)[1:]
+        # Build self.columns.
+        for i in range(0,len(splits),2):
+            c = Column_OLD()
+            c.colalign = self.ALIGNMENTS[splits[i]]
+            s = splits[i+1]
+            if self.isnumeric:
+                # Strip trailing fillchars.
+                s = re.sub(fc+r'+$','',s)
+                if s == '':
+                    c.rulerwidth = None
+                else:
+                    c.rulerwidth = int(validate(s,'int($)>0',
+                        'malformed ruler: bad width'))
+            else:   # Calculate column width from inter-fillchar intervals.
+                if not re.match(r'^'+fc+r'+$',s):
+                    raise EAsciiDoc,'malformed ruler: illegal fillchars'
+                c.rulerwidth = len(s)+1
+            self.columns.append(c)
+        # Fill in unspecified ruler widths.
+        if self.isnumeric:
+            if self.columns[0].rulerwidth is None:
+                prevwidth = 1
+            for c in self.columns:
+                if c.rulerwidth is None:
+                    c.rulerwidth = prevwidth
+                prevwidth = c.rulerwidth
+    def build_colspecs(self):
+        """Generate colwidths and colspecs. This can only be done after the
+        table arguments have been parsed since we use the table format."""
+        self.attributes['cols'] = len(self.columns)
+        # Calculate total ruler width.
+        totalwidth = 0
+        for c in self.columns:
+            totalwidth = totalwidth + c.rulerwidth
+        if totalwidth <= 0:
+            raise EAsciiDoc,'zero width table'
+        # Calculate marked up colwidths from rulerwidths.
+        for c in self.columns:
+            # Convert ruler width to output page width.
+            width = float(c.rulerwidth)
+            if self.format == 'fixed':
+                if self.tablewidth is None:
+                    # Size proportional to ruler width.
+                    colfraction = width/config.textwidth
+                else:
+                    # Size proportional to page width.
+                    colfraction = width/totalwidth
+            else:
+                    # Size proportional to page width.
+                colfraction = width/totalwidth
+            c.colwidth = colfraction * config.pagewidth # To page units.
+            if self.tablewidth is not None:
+                c.colwidth = c.colwidth * self.tablewidth   # Scale factor.
+                if self.tablewidth > 1:
+                    c.colwidth = c.colwidth/100 # tablewidth is in percent.
+        # Build colspecs.
+        if self.colspec:
+            cols = []
+            i = 0
+            for c in self.columns:
+                i += 1
+                self.attributes['colalign'] = c.colalign
+                self.attributes['colwidth'] = str(int(c.colwidth))
+                self.attributes['colnumber'] = str(i + 1)
+                s = subs_attrs(self.colspec,self.attributes)
+                if not s:
+                    message.warning('colspec dropped: contains undefined attribute')
+                else:
+                    cols.append(s)
+            self.attributes['colspecs'] = writer.newline.join(cols)
+    def split_rows(self,rows):
+        """Return a two item tuple containing a list of lines up to but not
+        including the next underline (continued lines are joined ) and the
+        tuple of all lines after the underline."""
+        reo = re.compile(self.underline)
+        i = 0
+        while not reo.match(rows[i]):
+            i = i+1
+        if i == 0:
+            raise EAsciiDoc,'missing table rows'
+        if i >= len(rows):
+            raise EAsciiDoc,'closing [%s] underline expected' % self.name
+        return (join_lines_OLD(rows[:i]), rows[i+1:])
+    def parse_rows(self, rows, rtag, dtag):
+        """Parse rows list using the row and data tags. Returns a substituted
+        list of output lines."""
+        result = []
+        # Source rows are parsed as single block, rather than line by line, to
+        # allow the CSV reader to handle multi-line rows.
+        if self.format == 'fixed':
+            rows = self.parse_fixed(rows)
+        elif self.format == 'csv':
+            rows = self.parse_csv(rows)
+        elif self.format == 'dsv':
+            rows = self.parse_dsv(rows)
+        else:
+            assert True,'illegal table format'
+        # Substitute and indent all data in all rows.
+        stag,etag = subs_tag(rtag,self.attributes)
+        for row in rows:
+            result.append('  '+stag)
+            for data in self.subs_row(row,dtag):
+                result.append('    '+data)
+            result.append('  '+etag)
+        return result
+    def subs_row(self, data, dtag):
+        """Substitute the list of source row data elements using the data tag.
+        Returns a substituted list of output table data items."""
+        result = []
+        if len(data) < len(self.columns):
+            message.warning('fewer row data items then table columns')
+        if len(data) > len(self.columns):
+            message.warning('more row data items than table columns')
+        for i in range(len(self.columns)):
+            if i > len(data) - 1:
+                d = ''  # Fill missing column data with blanks.
+            else:
+                d = data[i]
+            c = self.columns[i]
+            self.attributes['colalign'] = c.colalign
+            self.attributes['colwidth'] = str(int(c.colwidth))
+            self.attributes['colnumber'] = str(i + 1)
+            stag,etag = subs_tag(dtag,self.attributes)
+            # Insert AsciiDoc line break (' +') where row data has newlines
+            # ('\n').  This is really only useful when the table format is csv
+            # and the output markup is HTML. It's also a bit dubious in that it
+            # assumes the user has not modified the shipped line break pattern.
+            subs = self.get_subs()[0]
+            if 'replacements' in subs:
+                # Insert line breaks in cell data.
+                d = re.sub(r'(?m)\n',r' +\n',d)
+                d = d.split('\n')    # So writer.newline is written.
+            else:
+                d = [d]
+            result = result + [stag] + Lex.subs(d,subs) + [etag]
+        return result
+    def parse_fixed(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        result = []
+        for row in rows:
+            data = []
+            start = 0
+            # build an encoded representation
+            row = char_decode(row)
+            for c in self.columns:
+                end = start + c.rulerwidth
+                if c is self.columns[-1]:
+                    # Text in last column can continue forever.
+                    # Use the encoded string to slice, but convert back
+                    # to plain string before further processing
+                    data.append(char_encode(row[start:]).strip())
+                else:
+                    data.append(char_encode(row[start:end]).strip())
+                start = end
+            result.append(data)
+        return result
+    def parse_csv(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        import StringIO
+        import csv
+        result = []
+        rdr = csv.reader(StringIO.StringIO('\r\n'.join(rows)),
+            skipinitialspace=True)
+        try:
+            for row in rdr:
+                result.append(row)
+        except Exception:
+            raise EAsciiDoc,'csv parse error: %s' % row
+        return result
+    def parse_dsv(self,rows):
+        """Parse the list of source table rows. Each row item in the returned
+        list contains a list of cell data elements."""
+        separator = self.attributes.get('separator',':')
+        separator = eval('"'+separator+'"')
+        if len(separator) != 1:
+            raise EAsciiDoc,'malformed dsv separator: %s' % separator
+        # TODO If separator is preceeded by an odd number of backslashes then
+        # it is escaped and should not delimit.
+        result = []
+        for row in rows:
+            # Skip blank lines
+            if row == '': continue
+            # Unescape escaped characters.
+            row = eval('"'+row.replace('"','\\"')+'"')
+            data = row.split(separator)
+            data = [s.strip() for s in data]
+            result.append(data)
+        return result
+    def translate(self):
+        message.deprecated('old tables syntax')
+        AbstractBlock.translate(self)
+        # Reset instance specific properties.
+        self.underline = None
+        self.columns = []
+        attrs = {}
+        BlockTitle.consume(attrs)
+        # Add relevant globals to table substitutions.
+        attrs['pagewidth'] = str(config.pagewidth)
+        attrs['pageunits'] = config.pageunits
+        # Mix in document attribute list.
+        AttributeList.consume(attrs)
+        # Validate overridable attributes.
+        for k,v in attrs.items():
+            if k == 'format':
+                if v not in self.FORMATS:
+                    raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.name,k,v)
+                self.format = v
+            elif k == 'tablewidth':
+                try:
+                    self.tablewidth = float(attrs['tablewidth'])
+                except Exception:
+                    raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.name,k,v)
+        self.merge_attributes(attrs)
+        # Parse table ruler.
+        ruler = reader.read()
+        assert re.match(self.delimiter,ruler)
+        self.parse_ruler(ruler)
+        # Read the entire table.
+        table = []
+        while True:
+            line = reader.read_next()
+            # Table terminated by underline followed by a blank line or EOF.
+            if len(table) > 0 and re.match(self.underline,table[-1]):
+                if line in ('',None):
+                    break;
+            if line is None:
+                raise EAsciiDoc,'closing [%s] underline expected' % self.name
+            table.append(reader.read())
+        # EXPERIMENTAL: The number of lines in the table, requested by Benjamin Klum.
+        self.attributes['rows'] = str(len(table))
+        if self.check_msg:  # Skip if table definition was marked invalid.
+            message.warning('skipping %s table: %s' % (self.name,self.check_msg))
+            return
+        # Generate colwidths and colspecs.
+        self.build_colspecs()
+        # Generate headrows, footrows, bodyrows.
+        # Headrow, footrow and bodyrow data replaces same named attributes in
+        # the table markup template. In order to ensure this data does not get
+        # a second attribute substitution (which would interfere with any
+        # already substituted inline passthroughs) unique placeholders are used
+        # (the tab character does not appear elsewhere since it is expanded on
+        # input) which are replaced after template attribute substitution.
+        headrows = footrows = []
+        bodyrows,table = self.split_rows(table)
+        if table:
+            headrows = bodyrows
+            bodyrows,table = self.split_rows(table)
+            if table:
+                footrows,table = self.split_rows(table)
+        if headrows:
+            headrows = self.parse_rows(headrows, self.headrow, self.headdata)
+            headrows = writer.newline.join(headrows)
+            self.attributes['headrows'] = '\x07headrows\x07'
+        if footrows:
+            footrows = self.parse_rows(footrows, self.footrow, self.footdata)
+            footrows = writer.newline.join(footrows)
+            self.attributes['footrows'] = '\x07footrows\x07'
+        bodyrows = self.parse_rows(bodyrows, self.bodyrow, self.bodydata)
+        bodyrows = writer.newline.join(bodyrows)
+        self.attributes['bodyrows'] = '\x07bodyrows\x07'
+        table = subs_attrs(config.sections[self.template],self.attributes)
+        table = writer.newline.join(table)
+        # Before we finish replace the table head, foot and body place holders
+        # with the real data.
+        if headrows:
+            table = table.replace('\x07headrows\x07', headrows, 1)
+        if footrows:
+            table = table.replace('\x07footrows\x07', footrows, 1)
+        table = table.replace('\x07bodyrows\x07', bodyrows, 1)
+        writer.write(table,trace='table')
+
+class Tables_OLD(AbstractBlocks):
+    """List of tables."""
+    BLOCK_TYPE = Table_OLD
+    PREFIX = 'old_tabledef-'
+    def __init__(self):
+        AbstractBlocks.__init__(self)
+    def load(self,sections):
+        AbstractBlocks.load(self,sections)
+    def validate(self):
+        # Does not call AbstractBlocks.validate().
+        # Check we have a default table definition,
+        for i in range(len(self.blocks)):
+            if self.blocks[i].name == 'old_tabledef-default':
+                default = self.blocks[i]
+                break
+        else:
+            raise EAsciiDoc,'missing section: [OLD_tabledef-default]'
+        # Set default table defaults.
+        if default.format is None: default.subs = 'fixed'
+        # Propagate defaults to unspecified table parameters.
+        for b in self.blocks:
+            if b is not default:
+                if b.fillchar is None: b.fillchar = default.fillchar
+                if b.format is None: b.format = default.format
+                if b.template is None: b.template = default.template
+                if b.colspec is None: b.colspec = default.colspec
+                if b.headrow is None: b.headrow = default.headrow
+                if b.footrow is None: b.footrow = default.footrow
+                if b.bodyrow is None: b.bodyrow = default.bodyrow
+                if b.headdata is None: b.headdata = default.headdata
+                if b.footdata is None: b.footdata = default.footdata
+                if b.bodydata is None: b.bodydata = default.bodydata
+        # Check all tables have valid fill character.
+        for b in self.blocks:
+            if not b.fillchar or len(b.fillchar) != 1:
+                raise EAsciiDoc,'[%s] missing or illegal fillchar' % b.name
+        # Build combined tables delimiter patterns and assign defaults.
+        delimiters = []
+        for b in self.blocks:
+            # Ruler is:
+            #   (ColStop,(ColWidth,FillChar+)?)+, FillChar+, TableWidth?
+            b.delimiter = r'^(' + Table_OLD.COL_STOP \
+                + r'(\d*|' + re.escape(b.fillchar) + r'*)' \
+                + r')+' \
+                + re.escape(b.fillchar) + r'+' \
+                + '([\d\.]*)$'
+            delimiters.append(b.delimiter)
+            if not b.headrow:
+                b.headrow = b.bodyrow
+            if not b.footrow:
+                b.footrow = b.bodyrow
+            if not b.headdata:
+                b.headdata = b.bodydata
+            if not b.footdata:
+                b.footdata = b.bodydata
+        self.delimiters = re_join(delimiters)
+        # Check table definitions are valid.
+        for b in self.blocks:
+            b.validate()
+            if config.verbose:
+                if b.check_msg:
+                    message.warning('[%s] table definition: %s' % (b.name,b.check_msg))
+
+# End of deprecated old table classes.
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# Filter commands.
+#---------------------------------------------------------------------------
+import shutil, zipfile
+
+def die(msg):
+    message.stderr(msg)
+    sys.exit(1)
+
+def unzip(zip_file, destdir):
+    """
+    Unzip Zip file to destination directory.
+    Throws exception if error occurs.
+    """
+    zipo = zipfile.ZipFile(zip_file, 'r')
+    try:
+        for zi in zipo.infolist():
+            outfile = zi.filename
+            if not outfile.endswith('/'):
+                d, outfile = os.path.split(outfile)
+                directory = os.path.normpath(os.path.join(destdir, d))
+                if not os.path.isdir(directory):
+                    os.makedirs(directory)
+                outfile = os.path.join(directory, outfile)
+                perms = (zi.external_attr >> 16) & 0777
+                message.verbose('extracting: %s' % outfile)
+                fh = os.open(outfile, os.O_CREAT | os.O_WRONLY, perms)
+                try:
+                    os.write(fh, zipo.read(zi.filename))
+                finally:
+                    os.close(fh)
+    finally:
+        zipo.close()
+
+class Filter:
+    """
+    --filter option commands.
+    """
+
+    @staticmethod
+    def get_filters_dir():
+        """
+        Return path of .asciidoc/filters in user's home direcory or None if
+        user home not defined.
+        """
+        result = userdir()
+        if result:
+            result = os.path.join(result,'.asciidoc','filters')
+        return result
+
+    @staticmethod
+    def install(args):
+        """
+        Install filter Zip file.
+        args[0] is filter zip file path.
+        args[1] is optional destination filters directory.
+        """
+        if len(args) not in (1,2):
+            die('invalid number of arguments: --filter install %s'
+                    % ' '.join(args))
+        zip_file = args[0]
+        if not os.path.isfile(zip_file):
+            die('file not found: %s' % zip_file)
+        reo = re.match(r'^\w+',os.path.split(zip_file)[1])
+        if not reo:
+            die('filter file name does not start with legal filter name: %s'
+                    % zip_file)
+        filter_name = reo.group()
+        if len(args) == 2:
+            filters_dir = args[1]
+            if not os.path.isdir(filters_dir):
+                die('directory not found: %s' % filters_dir)
+        else:
+            filters_dir = Filter.get_filters_dir()
+            if not filters_dir:
+                die('user home directory is not defined')
+        filter_dir = os.path.join(filters_dir, filter_name)
+        if os.path.exists(filter_dir):
+            die('filter is already installed: %s' % filter_dir)
+        try:
+            os.makedirs(filter_dir)
+        except Exception,e:
+            die('failed to create filter directory: %s' % str(e))
+        try:
+            unzip(zip_file, filter_dir)
+        except Exception,e:
+            die('failed to extract filter: %s' % str(e))
+
+    @staticmethod
+    def remove(args):
+        """
+        Delete filter from .asciidoc/filters/ in user's home directory.
+        args[0] is filter name.
+        args[1] is optional filters directory.
+        """
+        if len(args) not in (1,2):
+            die('invalid number of arguments: --filter remove %s'
+                    % ' '.join(args))
+        filter_name = args[0]
+        if not re.match(r'^\w+$',filter_name):
+            die('illegal filter name: %s' % filter_name)
+        if len(args) == 2:
+            d = args[1]
+            if not os.path.isdir(d):
+                die('directory not found: %s' % d)
+        else:
+            d = Filter.get_filters_dir()
+            if not d:
+                die('user directory is not defined')
+        filter_dir = os.path.join(d, filter_name)
+        if not os.path.isdir(filter_dir):
+            die('cannot find filter: %s' % filter_dir)
+        try:
+            message.verbose('removing: %s' % filter_dir)
+            shutil.rmtree(filter_dir)
+        except Exception,e:
+            die('failed to delete filter: %s' % str(e))
+
+    @staticmethod
+    def list():
+        """
+        List all filter directories (global and local).
+        """
+        for d in [os.path.join(d,'filters') for d in config.get_load_dirs()]:
+            if os.path.isdir(d):
+                for f in os.walk(d).next()[1]:
+                    message.stdout(os.path.join(d,f))
+
+
+#---------------------------------------------------------------------------
+# Application code.
+#---------------------------------------------------------------------------
+# Constants
+# ---------
+APP_FILE = None             # This file's full path.
+APP_DIR = None              # This file's directory.
+USER_DIR = None             # ~/.asciidoc
+# Global configuration files directory (set by Makefile build target).
+CONF_DIR = '/etc/asciidoc'
+HELP_FILE = 'help.conf'     # Default (English) help file.
+
+# Globals
+# -------
+document = Document()       # The document being processed.
+config = Config()           # Configuration file reader.
+reader = Reader()           # Input stream line reader.
+writer = Writer()           # Output stream line writer.
+message = Message()         # Message functions.
+paragraphs = Paragraphs()   # Paragraph definitions.
+lists = Lists()             # List definitions.
+blocks = DelimitedBlocks()  # DelimitedBlock definitions.
+tables_OLD = Tables_OLD()   # Table_OLD definitions.
+tables = Tables()           # Table definitions.
+macros = Macros()           # Macro definitions.
+calloutmap = CalloutMap()   # Coordinates callouts and callout list.
+trace = Trace()             # Implements trace attribute processing.
+
+### Used by asciidocapi.py ###
+# List of message strings written to stderr.
+messages = message.messages
+
+
+def asciidoc(backend, doctype, confiles, infile, outfile, options):
+    """Convert AsciiDoc document to DocBook document of type doctype
+    The AsciiDoc document is read from file object src the translated
+    DocBook file written to file object dst."""
+    def load_conffiles(include=[], exclude=[]):
+        # Load conf files specified on the command-line and by the conf-files attribute.
+        files = document.attributes.get('conf-files','')
+        files = [f.strip() for f in files.split('|') if f.strip()]
+        files += confiles
+        if files:
+            for f in files:
+                if os.path.isfile(f):
+                    config.load_file(f, include=include, exclude=exclude)
+                else:
+                    raise EAsciiDoc,'configuration file %s missing' % f
+
+    try:
+        if doctype not in (None,'article','manpage','book'):
+            raise EAsciiDoc,'illegal document type'
+        # Set processing options.
+        for o in options:
+            if o == '-c': config.dumping = True
+            if o == '-s': config.header_footer = False
+            if o == '-v': config.verbose = True
+        document.update_attributes()
+        if '-e' not in options:
+            # Load asciidoc.conf files in two passes: the first for attributes
+            # the second for everything. This is so that locally set attributes
+            # available are in the global asciidoc.conf
+            if not config.load_from_dirs('asciidoc.conf',include=['attributes']):
+                raise EAsciiDoc,'configuration file asciidoc.conf missing'
+            load_conffiles(include=['attributes'])
+            config.load_from_dirs('asciidoc.conf')
+            if infile != '<stdin>':
+                indir = os.path.dirname(infile)
+                config.load_file('asciidoc.conf', indir,
+                                include=['attributes','titles','specialchars'])
+        else:
+            load_conffiles(include=['attributes','titles','specialchars'])
+        document.update_attributes()
+        # Check the infile exists.
+        if infile != '<stdin>':
+            if not os.path.isfile(infile):
+                raise EAsciiDoc,'input file %s missing' % infile
+        document.infile = infile
+        AttributeList.initialize()
+        # Open input file and parse document header.
+        reader.tabsize = config.tabsize
+        reader.open(infile)
+        has_header = document.parse_header(doctype,backend)
+        # doctype is now finalized.
+        document.attributes['doctype-'+document.doctype] = ''
+        # Load backend configuration files.
+        if '-e' not in options:
+            f = document.backend + '.conf'
+            if not config.find_in_dirs(f):
+                message.warning('missing backend conf file: %s' % f, linenos=False)
+            config.load_backend()
+        # backend is now known.
+        document.attributes['backend-'+document.backend] = ''
+        document.attributes[document.backend+'-'+document.doctype] = ''
+        doc_conffiles = []
+        if '-e' not in options:
+            # Load filters and language file.
+            config.load_filters()
+            document.load_lang()
+            if infile != '<stdin>':
+                # Load local conf files (files in the source file directory).
+                config.load_file('asciidoc.conf', indir)
+                config.load_backend([indir])
+                config.load_filters([indir])
+                # Load document specific configuration files.
+                f = os.path.splitext(infile)[0]
+                doc_conffiles = [
+                        f for f in (f+'.conf', f+'-'+document.backend+'.conf')
+                        if os.path.isfile(f) ]
+                for f in doc_conffiles:
+                    config.load_file(f)
+        load_conffiles()
+        # Build asciidoc-args attribute.
+        args = ''
+        # Add custom conf file arguments.
+        for f in doc_conffiles + confiles:
+            args += ' --conf-file "%s"' % f
+        # Add command-line and header attributes.
+        attrs = {}
+        attrs.update(AttributeEntry.attributes)
+        attrs.update(config.cmd_attrs)
+        if 'title' in attrs:    # Don't pass the header title.
+            del attrs['title']
+        for k,v in attrs.items():
+            if v:
+                args += ' --attribute "%s=%s"' % (k,v)
+            else:
+                args += ' --attribute "%s"' % k
+        document.attributes['asciidoc-args'] = args
+        # Build outfile name.
+        if outfile is None:
+            outfile = os.path.splitext(infile)[0] + '.' + document.backend
+            if config.outfilesuffix:
+                # Change file extension.
+                outfile = os.path.splitext(outfile)[0] + config.outfilesuffix
+        document.outfile = outfile
+        # Document header attributes override conf file attributes.
+        document.attributes.update(AttributeEntry.attributes)
+        document.update_attributes()
+        # Configuration is fully loaded so can expand templates.
+        config.expand_all_templates()
+        # Check configuration for consistency.
+        config.validate()
+        paragraphs.initialize()
+        lists.initialize()
+        if config.dumping:
+            config.dump()
+        else:
+            writer.newline = config.newline
+            try:
+                writer.open(outfile, reader.bom)
+                try:
+                    document.translate(has_header) # Generate the output.
+                finally:
+                    writer.close()
+            finally:
+                reader.closefile()
+    except KeyboardInterrupt:
+        raise
+    except Exception,e:
+        # Cleanup.
+        if outfile and outfile != '<stdout>' and os.path.isfile(outfile):
+            os.unlink(outfile)
+        # Build and print error description.
+        msg = 'FAILED: '
+        if reader.cursor:
+            msg = message.format('', msg)
+        if isinstance(e, EAsciiDoc):
+            message.stderr('%s%s' % (msg,str(e)))
+        else:
+            if __name__ == '__main__':
+                message.stderr(msg+'unexpected error:')
+                message.stderr('-'*60)
+                traceback.print_exc(file=sys.stderr)
+                message.stderr('-'*60)
+            else:
+                message.stderr('%sunexpected error: %s' % (msg,str(e)))
+        sys.exit(1)
+
+def usage(msg=''):
+    if msg:
+        message.stderr(msg)
+    show_help('default', sys.stderr)
+
+def show_help(topic, f=None):
+    """Print help topic to file object f."""
+    if f is None:
+        f = sys.stdout
+    # Select help file.
+    lang = config.cmd_attrs.get('lang')
+    if lang and lang != 'en':
+        help_file = 'help-' + lang + '.conf'
+    else:
+        help_file = HELP_FILE
+    # Print [topic] section from help file.
+    config.load_from_dirs(help_file)
+    if len(config.sections) == 0:
+        # Default to English if specified language help files not found.
+        help_file = HELP_FILE
+        config.load_from_dirs(help_file)
+    if len(config.sections) == 0:
+        message.stderr('no help topics found')
+        sys.exit(1)
+    n = 0
+    for k in config.sections:
+        if re.match(re.escape(topic), k):
+            n += 1
+            lines = config.sections[k]
+    if n == 0:
+        if topic != 'topics':
+            message.stderr('help topic not found: [%s] in %s' % (topic, help_file))
+        message.stderr('available help topics: %s' % ', '.join(config.sections.keys()))
+        sys.exit(1)
+    elif n > 1:
+        message.stderr('ambiguous help topic: %s' % topic)
+    else:
+        for line in lines:
+            print >>f, line
+
+### Used by asciidocapi.py ###
+def execute(cmd,opts,args):
+    """
+    Execute asciidoc with command-line options and arguments.
+    cmd is asciidoc command or asciidoc.py path.
+    opts and args conform to values returned by getopt.getopt().
+    Raises SystemExit if an error occurs.
+
+    Doctests:
+
+    1. Check execution:
+
+       >>> import StringIO
+       >>> infile = StringIO.StringIO('Hello *{author}*')
+       >>> outfile = StringIO.StringIO()
+       >>> opts = []
+       >>> opts.append(('--backend','html4'))
+       >>> opts.append(('--no-header-footer',None))
+       >>> opts.append(('--attribute','author=Joe Bloggs'))
+       >>> opts.append(('--out-file',outfile))
+       >>> execute(__file__, opts, [infile])
+       >>> print outfile.getvalue()
+       <p>Hello <strong>Joe Bloggs</strong></p>
+
+       >>>
+
+    """
+    config.init(cmd)
+    if len(args) > 1:
+        usage('To many arguments')
+        sys.exit(1)
+    backend = None
+    doctype = None
+    confiles = []
+    outfile = None
+    options = []
+    help_option = False
+    for o,v in opts:
+        if o in ('--help','-h'):
+            help_option = True
+        #DEPRECATED: --unsafe option.
+        if o == '--unsafe':
+            document.safe = False
+        if o == '--safe':
+            document.safe = True
+        if o == '--version':
+            print('asciidoc %s' % VERSION)
+            sys.exit(0)
+        if o in ('-b','--backend'):
+            backend = v
+#            config.cmd_attrs['backend'] = v
+        if o in ('-c','--dump-conf'):
+            options.append('-c')
+        if o in ('-d','--doctype'):
+            doctype = v
+#            config.cmd_attrs['doctype'] = v
+        if o in ('-e','--no-conf'):
+            options.append('-e')
+        if o in ('-f','--conf-file'):
+            confiles.append(v)
+        if o in ('-n','--section-numbers'):
+            o = '-a'
+            v = 'numbered'
+        if o in ('-a','--attribute'):
+            e = parse_entry(v, allow_name_only=True)
+            if not e:
+                usage('Illegal -a option: %s' % v)
+                sys.exit(1)
+            k,v = e
+            # A @ suffix denotes don't override existing document attributes.
+            if v and v[-1] == '@':
+                document.attributes[k] = v[:-1]
+            else:
+                config.cmd_attrs[k] = v
+        if o in ('-o','--out-file'):
+            outfile = v
+        if o in ('-s','--no-header-footer'):
+            options.append('-s')
+        if o in ('-v','--verbose'):
+            options.append('-v')
+    if help_option:
+        if len(args) == 0:
+            show_help('default')
+        else:
+            show_help(args[-1])
+        sys.exit(0)
+    if len(args) == 0 and len(opts) == 0:
+        usage()
+        sys.exit(0)
+    if len(args) == 0:
+        usage('No source file specified')
+        sys.exit(1)
+#    if not backend:
+#        usage('No --backend option specified')
+#        sys.exit(1)
+    stdin,stdout = sys.stdin,sys.stdout
+    try:
+        infile = args[0]
+        if infile == '-':
+            infile = '<stdin>'
+        elif isinstance(infile, str):
+            infile = os.path.abspath(infile)
+        else:   # Input file is file object from API call.
+            sys.stdin = infile
+            infile = '<stdin>'
+        if outfile == '-':
+            outfile = '<stdout>'
+        elif isinstance(outfile, str):
+            outfile = os.path.abspath(outfile)
+        elif outfile is None:
+            if infile == '<stdin>':
+                outfile = '<stdout>'
+        else:   # Output file is file object from API call.
+            sys.stdout = outfile
+            outfile = '<stdout>'
+        # Do the work.
+        asciidoc(backend, doctype, confiles, infile, outfile, options)
+        if document.has_errors:
+            sys.exit(1)
+    finally:
+        sys.stdin,sys.stdout = stdin,stdout
+
+if __name__ == '__main__':
+    # Process command line options.
+    import getopt
+    try:
+        #DEPRECATED: --unsafe option.
+        opts,args = getopt.getopt(sys.argv[1:],
+            'a:b:cd:ef:hno:svw:',
+            ['attribute=','backend=','conf-file=','doctype=','dump-conf',
+            'help','no-conf','no-header-footer','out-file=',
+            'section-numbers','verbose','version','safe','unsafe',
+            'doctest','filter'])
+    except getopt.GetoptError:
+        message.stderr('illegal command options')
+        sys.exit(1)
+    if '--doctest' in [opt[0] for opt in opts]:
+        # Run module doctests.
+        import doctest
+        options = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS
+        failures,tries = doctest.testmod(optionflags=options)
+        if failures == 0:
+            message.stderr('All doctests passed')
+            sys.exit(0)
+        else:
+            sys.exit(1)
+    if '--filter' in [opt[0] for opt in opts]:
+        config.init(sys.argv[0])
+        config.verbose = bool(set(['-v','--verbose']) & set([opt[0] for opt in opts]))
+        if not args:
+            die('missing --filter command')
+        elif args[0] == 'install':
+            Filter.install(args[1:])
+        elif args[0] == 'remove':
+            Filter.remove(args[1:])
+        elif args[0] == 'list':
+            Filter.list()
+        else:
+            die('illegal --filter command: %s' % args[0])
+        sys.exit(0)
+    try:
+        execute(sys.argv[0],opts,args)
+    except KeyboardInterrupt:
+        sys.exit(1)
diff --git a/doc/www/create-relnotes.sh b/doc/www/create-relnotes.sh
new file mode 100755 (executable)
index 0000000..9e731e8
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+# 
+# This script requires asciidoc http://www.methods.co.nz/asciidoc/
+
+# (echo -e "Netatalk NEWS\n-------------\n\n" ; cat NEWS) | ./asciidoc.py -f netatalk-news.conf -b css -o NEWS.html - && chmod g+w NEWS.html
+./asciidoc.py -f netatalk-relnotes.conf -b html5 -o ReleaseNotes.html ReleaseNotes && chmod g+w ReleaseNotes.html
diff --git a/doc/www/html5.conf b/doc/www/html5.conf
new file mode 100644 (file)
index 0000000..cedc3fd
--- /dev/null
@@ -0,0 +1,686 @@
+#
+# html5.conf
+#
+# Asciidoc configuration file.
+# html5 backend.
+#
+
+[miscellaneous]
+outfilesuffix=.html
+
+[attributes]
+basebackend=html
+basebackend-html=
+basebackend-html5=
+
+[replacements2]
+# Line break.
+(?m)^(.*)\s\+$=\1<br>
+
+[replacements]
+ifdef::asciidoc7compatible[]
+# Superscripts.
+\^(.+?)\^=<sup>\1</sup>
+# Subscripts.
+~(.+?)~=<sub>\1</sub>
+endif::asciidoc7compatible[]
+
+[ruler-blockmacro]
+<hr>
+
+[pagebreak-blockmacro]
+<div style="page-break-after:always"></div>
+
+[blockdef-pass]
+asciimath-style=template="asciimathblock",subs=[]
+latexmath-style=template="latexmathblock",subs=[]
+
+[macros]
+(?u)^(?P<name>audio|video)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
+# math macros.
+# Special characters are escaped in HTML math markup.
+(?su)[\\]?(?P<name>asciimath|latexmath):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[specialcharacters]
+(?u)^(?P<name>asciimath|latexmath)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#[specialcharacters]
+
+[asciimath-inlinemacro]
+`{passtext}`
+
+[asciimath-blockmacro]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+`{passtext}`
+</div></div>
+
+[asciimathblock]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+`|`
+</div></div>
+
+[latexmath-inlinemacro]
+{passtext}
+
+[latexmath-blockmacro]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+{passtext}
+</div></div>
+
+[latexmathblock]
+<div class="mathblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+|
+</div></div>
+
+[image-inlinemacro]
+<span class="image{role? {role}}">
+<a class="image" href="{link}">
+{data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"}>
+{data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
+{data-uri#}{sys3:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}">
+{link#}</a>
+</span>
+
+[image-blockmacro]
+<div class="imageblock{style? {style}}{role? {role}}"{id? id="{id}"}{align? style="text-align:{align};"}{float? style="float:{float};"}>
+<div class="content">
+<a class="image" href="{link}">
+{data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}>
+{data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
+{data-uri#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}">
+{link#}</a>
+</div>
+<div class="title">{caption={figure-caption} {counter:figure-number}. }{title}</div>
+</div>
+
+[audio-blockmacro]
+<div class="audioblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content">
+<audio src="{imagesdir=}{imagesdir?/}{target}"{autoplay-option? autoplay}{nocontrols-option! controls}{loop-option? loop}>
+Your browser does not support the audio tag.
+</audio>
+</div></div>
+
+[video-blockmacro]
+<div class="videoblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content">
+<video src="{imagesdir=}{imagesdir?/}{target}"{width? width="{width}"}{height? height="{height}"}{poster? poster="{poster}"}{autoplay-option? autoplay}{nocontrols-option! controls}{loop-option? loop}>
+Your browser does not support the video tag.
+</video>
+</div></div>
+
+[unfloat-blockmacro]
+<div style="clear:both;"></div>
+
+[indexterm-inlinemacro]
+# Index term.
+{empty}
+
+[indexterm2-inlinemacro]
+# Index term.
+# Single entry index term that is visible in the primary text flow.
+{1}
+
+[footnote-inlinemacro]
+# footnote:[<text>].
+<span class="footnote"><br>[{0}]<br></span>
+
+[footnoteref-inlinemacro]
+# footnoteref:[<id>], create reference to footnote.
+{2%}<span class="footnoteref"><br><a href="#_footnote_{1}">[{1}]</a><br></span>
+# footnoteref:[<id>,<text>], create footnote with ID.
+{2#}<span class="footnote" id="_footnote_{1}"><br>[{2}]<br></span>
+
+[callout-inlinemacro]
+ifndef::icons[]
+<b>&lt;{index}&gt;</b>
+endif::icons[]
+ifdef::icons[]
+ifndef::data-uri[]
+<img src="{icon={iconsdir}/callouts/{index}.png}" alt="{index}">
+endif::data-uri[]
+ifdef::data-uri[]
+<img alt="{index}" src="data:image/png;base64,
+{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{index}.png}")}"}">
+endif::data-uri[]
+endif::icons[]
+
+# Comment line macros.
+[comment-inlinemacro]
+{showcomments#}<br><span class="comment">{passtext}</span><br>
+
+[comment-blockmacro]
+{showcomments#}<p><span class="comment">{passtext}</span></p>
+
+[literal-inlinemacro]
+# Inline literal.
+<span class="monospaced">{passtext}</span>
+
+# List tags.
+[listtags-bulleted]
+list=<div class="ulist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[listtags-numbered]
+# The start attribute is not valid XHTML 1.1 but all browsers support it.
+list=<div class="olist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol class="{style}"{start? start="{start}"}>|</ol></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[listtags-labeled]
+list=<div class="dlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
+entry=
+label=
+term=<dt class="hdlist1{strong-option? strong}">|</dt>
+item=<dd>|</dd>
+text=<p>|</p>
+
+[listtags-horizontal]
+list=<div class="hdlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>{labelwidth?<col width="{labelwidth}%">}{itemwidth?<col width="{itemwidth}%">}|</table></div>
+label=<td class="hdlist1{strong-option? strong}">|</td>
+term=|<br>
+entry=<tr>|</tr>
+item=<td class="hdlist2">|</td>
+text=<p style="margin-top: 0;">|</p>
+
+[listtags-qanda]
+list=<div class="qlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
+entry=<li>|</li>
+label=
+term=<p><em>|</em></p>
+item=
+text=<p>|</p>
+
+[listtags-callout]
+ifndef::icons[]
+list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
+item=<li>|</li>
+text=<p>|</p>
+endif::icons[]
+ifdef::icons[]
+list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>|</table></div>
+ifndef::data-uri[]
+item=<tr><td><img src="{iconsdir}/callouts/{listindex}.png" alt="{listindex}"></td><td>|</td></tr>
+endif::data-uri[]
+ifdef::data-uri[]
+item=<tr><td><img alt="{listindex}" src="data:image/png;base64, {sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{listindex}.png}")}"}"></td><td>|</td></tr>
+endif::data-uri[]
+text=|
+endif::icons[]
+
+[listtags-glossary]
+list=<div class="dlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
+label=
+entry=
+term=<dt>|</dt>
+item=<dd>|</dd>
+text=<p>|</p>
+
+[listtags-bibliography]
+list=<div class="ulist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
+item=<li>|</li>
+text=<p>|</p>
+
+[tags]
+# Quoted text.
+emphasis=<em>{1?<span class="{1}">}|{1?</span>}</em>
+strong=<strong>{1?<span class="{1}">}|{1?</span>}</strong>
+monospaced=<span class="monospaced{1? {1}}">|</span>
+singlequoted={lsquo}{1?<span class="{1}">}|{1?</span>}{rsquo}
+doublequoted={ldquo}{1?<span class="{1}">}|{1?</span>}{rdquo}
+unquoted={1?<span class="{1}">}|{1?</span>}
+superscript=<sup>{1?<span class="{1}">}|{1?</span>}</sup>
+subscript=<sub>{1?<span class="{1}">}|{1?</span>}</sub>
+
+ifdef::deprecated-quotes[]
+# Override with deprecated quote attributes.
+emphasis={role?<span class="{role}">}<em{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</em>{role?</span>}
+strong={role?<span class="{role}">}<strong{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</strong>{role?</span>}
+monospaced=<span class="monospaced{role? {role}}"{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</span>
+singlequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8216;|{amp}#8217;{1,2,3?</span>}{role?</span>}
+doublequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8220;|{amp}#8221;{1,2,3?</span>}{role?</span>}
+unquoted={role?<span class="{role}">}{1,2,3?<span style="{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}">}|{1,2,3?</span>}{role?</span>}
+superscript={role?<span class="{role}">}<sup{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sup>{role?</span>}
+subscript={role?<span class="{role}">}<sub{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sub>{role?</span>}
+endif::deprecated-quotes[]
+
+# Inline macros
+[http-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[https-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[ftp-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[file-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[irc-inlinemacro]
+<a href="{name}:{target}">{0={name}:{target}}</a>
+[mailto-inlinemacro]
+<a href="mailto:{target}">{0={target}}</a>
+[link-inlinemacro]
+<a href="{target}">{0={target}}</a>
+[callto-inlinemacro]
+<a href="{name}:{target}">{0={target}}</a>
+# anchor:id[text]
+[anchor-inlinemacro]
+<a id="{target}"></a>
+# [[id,text]]
+[anchor2-inlinemacro]
+<a id="{1}"></a>
+# [[[id]]]
+[anchor3-inlinemacro]
+<a id="{1}"></a>[{1}]
+# xref:id[text]
+[xref-inlinemacro]
+<a href="#{target}">{0=[{target}]}</a>
+# <<id,text>>
+[xref2-inlinemacro]
+<a href="#{1}">{2=[{1}]}</a>
+
+# Special word substitution.
+[emphasizedwords]
+<em>{words}</em>
+[monospacedwords]
+<span class="monospaced">{words}</span>
+[strongwords]
+<strong>{words}</strong>
+
+# Paragraph substitution.
+[paragraph]
+<div class="paragraph{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<p>
+|
+</p></div>
+
+[admonitionparagraph]
+template::[admonitionblock]
+
+# Delimited blocks.
+[listingblock]
+<div class="listingblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption=}{title}</div>
+<div class="content monospaced">
+<pre>
+|
+</pre>
+</div></div>
+
+[literalblock]
+<div class="literalblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content monospaced">
+<pre>
+|
+</pre>
+</div></div>
+
+[sidebarblock]
+<div class="sidebarblock{role? {role}}"{id? id="{id}"}>
+<div class="content">
+<div class="title">{title}</div>
+|
+</div></div>
+
+[openblock]
+<div class="openblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content">
+|
+</div></div>
+
+[partintroblock]
+template::[openblock]
+
+[abstractblock]
+template::[quoteblock]
+
+[quoteblock]
+<div class="quoteblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<div class="content">
+|
+</div>
+<div class="attribution">
+<em>{citetitle}</em>{attribution?<br>}
+&#8212; {attribution}
+</div></div>
+
+[verseblock]
+<div class="verseblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{title}</div>
+<pre class="content">
+|
+</pre>
+<div class="attribution">
+<em>{citetitle}</em>{attribution?<br>}
+&#8212; {attribution}
+</div></div>
+
+[exampleblock]
+<div class="exampleblock{role? {role}}"{id? id="{id}"}>
+<div class="title">{caption={example-caption} {counter:example-number}. }{title}</div>
+<div class="content">
+|
+</div></div>
+
+[admonitionblock]
+<div class="admonitionblock{role? {role}}"{id? id="{id}"}>
+<table><tr>
+<td class="icon">
+{data-uri%}{icons#}<img src="{icon={iconsdir}/{name}.png}" alt="{caption}">
+{data-uri#}{icons#}<img alt="{caption}" src="data:image/png;base64,
+{data-uri#}{icons#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/{name}.png}")}"}">
+{icons%}<div class="title">{caption}</div>
+</td>
+<td class="content">
+<div class="title">{title}</div>
+|
+</td>
+</tr></table>
+</div>
+
+# Tables.
+[tabletags-default]
+colspec=<col{autowidth-option! style="width:{colpcwidth}%;"}>
+bodyrow=<tr>|</tr>
+headdata=<th class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }>|</th>
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }>|</td>
+paragraph=<p class="tableblock">|</p>
+
+[tabletags-header]
+paragraph=<p class="tableblock header">|</p>
+
+[tabletags-emphasis]
+paragraph=<p class="tableblock"><em>|</em></p>
+
+[tabletags-strong]
+paragraph=<p class="tableblock"><strong>|</strong></p>
+
+[tabletags-monospaced]
+paragraph=<p class="tableblock monospaced">|</p>
+
+[tabletags-verse]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div class="verse">|</div></td>
+paragraph=
+
+[tabletags-literal]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div class="literal monospaced"><pre>|</pre></div></td>
+paragraph=
+
+[tabletags-asciidoc]
+bodydata=<td class="tableblock halign-{halign=left} valign-{valign=top}" {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }><div>|</div></td>
+paragraph=
+
+[table]
+<table class="tableblock frame-{frame=all} grid-{grid=all}{role? {role}}"{id? id="{id}"}
+style="
+margin-left:{align@left:0}{align@center|right:auto}; margin-right:{align@left|center:auto}{align@right:0};
+float:{float};
+{autowidth-option%}width:{tablepcwidth}%;
+{autowidth-option#}{width#style=width:{tablepcwidth}%;}
+">
+<caption class="title">{caption={table-caption} {counter:table-number}. }{title}</caption>
+{colspecs}
+{headrows#}<thead>
+{headrows}
+{headrows#}</thead>
+{footrows#}<tfoot>
+{footrows}
+{footrows#}</tfoot>
+<tbody>
+{bodyrows}
+</tbody>
+</table>
+
+#--------------------------------------------------------------------
+# Deprecated old table definitions.
+#
+
+[miscellaneous]
+# Screen width in pixels.
+pagewidth=800
+pageunits=px
+
+[old_tabledef-default]
+template=old_table
+colspec=<col style="width:{colwidth}{pageunits};" />
+bodyrow=<tr>|</tr>
+headdata=<th class="tableblock halign-{colalign=left}">|</th>
+footdata=<td class="tableblock halign-{colalign=left}">|</td>
+bodydata=<td class="tableblock halign-{colalign=left}">|</td>
+
+[old_table]
+<table class="tableblock frame-{frame=all} grid-{grid=all}"{id? id="{id}"}>
+<caption class="title">{caption={table-caption}}{title}</caption>
+{colspecs}
+{headrows#}<thead>
+{headrows}
+{headrows#}</thead>
+{footrows#}<tfoot>
+{footrows}
+{footrows#}</tfoot>
+<tbody style="vertical-align:top;">
+{bodyrows}
+</tbody>
+</table>
+
+# End of deprecated old table definitions.
+#--------------------------------------------------------------------
+
+[floatingtitle]
+<h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}{id? id="{id}"} class="float">{title}</h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}>
+
+[preamble]
+# Untitled elements between header and first section title.
+<div id="preamble">
+<div class="sectionbody">
+|
+</div>
+</div>
+
+# Document sections.
+[sect0]
+<h1{id? id="{id}"}>{title}</h1>
+|
+
+[sect1]
+<div class="sect1{style? {style}}{role? {role}}">
+<h2{id? id="{id}"}>{numbered?{sectnum} }{title}</h2>
+<div class="sectionbody">
+|
+</div>
+</div>
+
+[sect2]
+<div class="sect2{style? {style}}{role? {role}}">
+<h3{id? id="{id}"}>{numbered?{sectnum} }{title}</h3>
+|
+</div>
+
+[sect3]
+<div class="sect3{style? {style}}{role? {role}}">
+<h4{id? id="{id}"}>{numbered?{sectnum} }{title}</h4>
+|
+</div>
+
+[sect4]
+<div class="sect4{style? {style}}{role? {role}}">
+<h5{id? id="{id}"}>{title}</h5>
+|
+</div>
+
+[appendix]
+<div class="sect1{style? {style}}{role? {role}}">
+<h2{id? id="{id}"}>{numbered?{sectnum} }{appendix-caption} {counter:appendix-number:A}: {title}</h2>
+<div class="sectionbody">
+|
+</div>
+</div>
+
+[toc]
+<div id="toc">
+  <div id="toctitle">{toc-title}</div>
+  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
+</div>
+
+[header]
+<!DOCTYPE html>
+<html lang="{lang=en}">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={encoding}">
+<meta name="generator" content="AsciiDoc {asciidoc-version}">
+<meta name="description" content="{description}">
+<meta name="keywords" content="{keywords}">
+<title>{title}</title>
+{title%}<title>{doctitle=}</title>
+ifdef::linkcss[]
+<link rel="stylesheet" href="{stylesdir=.}/{theme=asciidoc}.css" type="text/css">
+{doctype-manpage}<link rel="stylesheet" href="{stylesdir=.}/{theme=asciidoc}-manpage.css" type="text/css">
+ifdef::pygments[<link rel="stylesheet" href="{stylesdir=.}/pygments.css" type="text/css">]
+ifdef::toc2[<link rel="stylesheet" href="{stylesdir=.}/toc2.css" type="text/css" />]
+<link rel="stylesheet" href="{stylesdir=.}/{stylesheet}" type="text/css">
+endif::linkcss[]
+ifndef::linkcss[]
+<style type="text/css">
+include1::{stylesdir=./stylesheets}/{theme=asciidoc}.css[]
+ifdef::doctype-manpage[]
+include1::{stylesdir=./stylesheets}/{theme=asciidoc}-manpage.css[]
+endif::doctype-manpage[]
+ifdef::pygments[]
+include1::{stylesdir=./stylesheets}/pygments.css[]
+endif::pygments[]
+ifdef::toc2[]
+include1::{stylesdir=./stylesheets}/toc2.css[]
+endif::toc2[]
+include1::{stylesheet}[]
+</style>
+endif::linkcss[]
+ifndef::disable-javascript[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/asciidoc.js"></script>
+<script type="text/javascript">
+#TODO: Escape not necessary in HTML5?
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+asciidoc.install({toc,toc2?{toclevels}});
+/*]]>*/
+</script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/asciidoc.js[]
+asciidoc.install({toc,toc2?{toclevels}});
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::disable-javascript[]
+ifdef::asciimath[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/ASCIIMathML.js"></script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/ASCIIMathML.js[]
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::asciimath[]
+ifdef::latexmath[]
+ifdef::linkcss[]
+<script type="text/javascript" src="{scriptsdir=.}/LaTeXMathML.js"></script>
+endif::linkcss[]
+ifndef::linkcss[]
+<script type="text/javascript">
+# Escape as CDATA to pass validators.
+/*<![CDATA[*/
+include1::{scriptsdir=./javascripts}/LaTeXMathML.js[]
+/*]]>*/
+</script>
+endif::linkcss[]
+endif::latexmath[]
+{docinfo1,docinfo2#}{include:{docdir}/docinfo.html}
+{docinfo,docinfo2#}{include:{docdir}/{docname}-docinfo.html}
+</head>
+<body class="{doctype}"{max-width? style="max-width:{max-width}"}>
+# Article, book header.
+ifndef::doctype-manpage[]
+<div id="header">
+ifndef::notitle[<h1>{doctitle}</h1>]
+ifdef::doctitle[]
+<span id="author">{author}</span><br>
+<span id="email" class="monospaced">&lt;<a href="mailto:{email}">{email}</a>&gt;</span><br>
+<span id="revnumber">version {revnumber}{revdate?,}</span>
+<span id="revdate">{revdate}</span>
+<br><span id="revremark">{revremark}</span>
+endif::doctitle[]
+ifdef::toc,toc2[{template:toc}]
+</div>
+endif::doctype-manpage[]
+# Man page header.
+ifdef::doctype-manpage[]
+<div id="header">
+<h1>
+{doctitle} Manual Page
+</h1>
+ifdef::toc,toc2[{template:toc}]
+<h2>{manname-title}</h2>
+<div class="sectionbody">
+<p>{manname} -
+   {manpurpose}
+</p>
+</div>
+</div>
+endif::doctype-manpage[]
+<div id="content">
+
+[footer]
+</div>
+{disable-javascript%<div id="footnotes"><hr></div>}
+<div id="footer">
+<div id="footer-text">
+template::[footer-text]
+</div>
+ifdef::badges[]
+<div id="footer-badges">
+ifndef::icons[]
+Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a>
+and <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>.
+endif::icons[]
+ifdef::icons[]
+<a href="http://validator.w3.org/check?uri=referer">
+  <img style="border:0;width:88px;height:31px"
+    src="http://www.w3.org/Icons/valid-xhtml11-blue"
+    alt="Valid XHTML 1.1" height="31" width="88">
+</a>
+<a href="http://jigsaw.w3.org/css-validator/">
+  <img style="border:0;width:88px;height:31px"
+    src="http://jigsaw.w3.org/css-validator/images/vcss-blue"
+    alt="Valid CSS!">
+</a>
+<a href="http://www.mozilla.org/products/firefox/">
+  <img style="border:none; width:110px; height:32px;"
+       src="http://www.spreadfirefox.com/community/images/affiliates/Buttons/110x32/safer.gif"
+       alt="Get Firefox!">
+</a>
+endif::icons[]
+</div>
+endif::badges[]
+</div>
+</body>
+</html>
+
+ifdef::doctype-manpage[]
+[synopsis]
+template::[sect1]
+endif::doctype-manpage[]
+
diff --git a/doc/www/javascripts/asciidoc.js b/doc/www/javascripts/asciidoc.js
new file mode 100644 (file)
index 0000000..2ad6c41
--- /dev/null
@@ -0,0 +1,189 @@
+var asciidoc = {  // Namespace.
+
+/////////////////////////////////////////////////////////////////////
+// Table Of Contents generator
+/////////////////////////////////////////////////////////////////////
+
+/* Author: Mihai Bazon, September 2002
+ * http://students.infoiasi.ro/~mishoo
+ *
+ * Table Of Content generator
+ * Version: 0.4
+ *
+ * Feel free to use this script under the terms of the GNU General Public
+ * License, as long as you do not remove or alter this notice.
+ */
+
+ /* modified by Troy D. Hanson, September 2006. License: GPL */
+ /* modified by Stuart Rackham, 2006, 2009. License: GPL */
+
+// toclevels = 1..4.
+toc: function (toclevels) {
+
+  function getText(el) {
+    var text = "";
+    for (var i = el.firstChild; i != null; i = i.nextSibling) {
+      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
+        text += i.data;
+      else if (i.firstChild != null)
+        text += getText(i);
+    }
+    return text;
+  }
+
+  function TocEntry(el, text, toclevel) {
+    this.element = el;
+    this.text = text;
+    this.toclevel = toclevel;
+  }
+
+  function tocEntries(el, toclevels) {
+    var result = new Array;
+    var re = new RegExp('[hH]([2-'+(toclevels+1)+'])');
+    // Function that scans the DOM tree for header elements (the DOM2
+    // nodeIterator API would be a better technique but not supported by all
+    // browsers).
+    var iterate = function (el) {
+      for (var i = el.firstChild; i != null; i = i.nextSibling) {
+        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
+          var mo = re.exec(i.tagName);
+          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
+            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
+          }
+          iterate(i);
+        }
+      }
+    }
+    iterate(el);
+    return result;
+  }
+
+  var toc = document.getElementById("toc");
+  if (!toc) {
+    return;
+  }
+
+  // Delete existing TOC entries in case we're reloading the TOC.
+  var tocEntriesToRemove = [];
+  var i;
+  for (i = 0; i < toc.childNodes.length; i++) {
+    var entry = toc.childNodes[i];
+    if (entry.nodeName == 'DIV'
+     && entry.getAttribute("class")
+     && entry.getAttribute("class").match(/^toclevel/))
+      tocEntriesToRemove.push(entry);
+  }
+  for (i = 0; i < tocEntriesToRemove.length; i++) {
+    toc.removeChild(tocEntriesToRemove[i]);
+  }
+  
+  // Rebuild TOC entries.
+  var entries = tocEntries(document.getElementById("content"), toclevels);
+  for (var i = 0; i < entries.length; ++i) {
+    var entry = entries[i];
+    if (entry.element.id == "")
+      entry.element.id = "_toc_" + i;
+    var a = document.createElement("a");
+    a.href = "#" + entry.element.id;
+    a.appendChild(document.createTextNode(entry.text));
+    var div = document.createElement("div");
+    div.appendChild(a);
+    div.className = "toclevel" + entry.toclevel;
+    toc.appendChild(div);
+  }
+  if (entries.length == 0)
+    toc.parentNode.removeChild(toc);
+},
+
+
+/////////////////////////////////////////////////////////////////////
+// Footnotes generator
+/////////////////////////////////////////////////////////////////////
+
+/* Based on footnote generation code from:
+ * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
+ */
+
+footnotes: function () {
+  // Delete existing footnote entries in case we're reloading the footnodes.
+  var i;
+  var noteholder = document.getElementById("footnotes");
+  if (!noteholder) {
+    return;
+  }
+  var entriesToRemove = [];
+  for (i = 0; i < noteholder.childNodes.length; i++) {
+    var entry = noteholder.childNodes[i];
+    if (entry.nodeName == 'DIV' && entry.getAttribute("class") == "footnote")
+      entriesToRemove.push(entry);
+  }
+  for (i = 0; i < entriesToRemove.length; i++) {
+    noteholder.removeChild(entriesToRemove[i]);
+  }
+
+  // Rebuild footnote entries.
+  var cont = document.getElementById("content");
+  var spans = cont.getElementsByTagName("span");
+  var refs = {};
+  var n = 0;
+  for (i=0; i<spans.length; i++) {
+    if (spans[i].className == "footnote") {
+      n++;
+      var note = spans[i].getAttribute("data-note");
+      if (!note) {
+        // Use [\s\S] in place of . so multi-line matches work.
+        // Because JavaScript has no s (dotall) regex flag.
+        note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
+        spans[i].innerHTML =
+          "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
+          "' title='View footnote' class='footnote'>" + n + "</a>]";
+        spans[i].setAttribute("data-note", note);
+      }
+      noteholder.innerHTML +=
+        "<div class='footnote' id='_footnote_" + n + "'>" +
+        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
+        n + "</a>. " + note + "</div>";
+      var id =spans[i].getAttribute("id");
+      if (id != null) refs["#"+id] = n;
+    }
+  }
+  if (n == 0)
+    noteholder.parentNode.removeChild(noteholder);
+  else {
+    // Process footnoterefs.
+    for (i=0; i<spans.length; i++) {
+      if (spans[i].className == "footnoteref") {
+        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
+        href = href.match(/#.*/)[0];  // Because IE return full URL.
+        n = refs[href];
+        spans[i].innerHTML =
+          "[<a href='#_footnote_" + n +
+          "' title='View footnote' class='footnote'>" + n + "</a>]";
+      }
+    }
+  }
+},
+
+install: function(toclevels) {
+  var timerId;
+
+  function reinstall() {
+    asciidoc.footnotes();
+    if (toclevels) {
+      asciidoc.toc(toclevels);
+    }
+  }
+
+  function reinstallAndRemoveTimer() {
+    clearInterval(timerId);
+    reinstall();
+  }
+
+  timerId = setInterval(reinstall, 500);
+  if (document.addEventListener)
+    document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
+  else
+    window.onload = reinstallAndRemoveTimer;
+}
+
+}
diff --git a/doc/www/lang-en.conf b/doc/www/lang-en.conf
new file mode 100644 (file)
index 0000000..9d1c6d4
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# AsciiDoc English language configuration file.
+#
+
+[attributes]
+# Captions, used by (X)HTML backends.
+# Captions on RHS are displayed in outputs.
+ifdef::basebackend-html[]
+
+caution-caption=Caution
+important-caption=Important
+note-caption=Note
+tip-caption=Tip
+warning-caption=Warning
+figure-caption=Figure
+table-caption=Table
+example-caption=Example
+toc-title=Table of Contents
+appendix-caption=Appendix
+# Man page NAME section title.
+manname-title=NAME
+
+[footer-text]
+Version {revnumber}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>}
+Last updated {docdate} {doctime}
+
+endif::basebackend-html[]
+
+
+[specialsections]
+# DocBook special sections.
+# The regular expression on LHS is matched against source titles.
+ifdef::basebackend-docbook[]
+
+ifdef::doctype-article[]
+^Abstract$=abstract
+endif::doctype-article[]
+
+ifdef::doctype-book[]
+^Colophon$=colophon
+^Dedication$=dedication
+^Preface$=preface
+endif::doctype-book[]
+
+^Index$=index
+^(Bibliography|References)$=bibliography
+^Glossary$=glossary
+^Appendix [A-Z][:.](?P<title>.*)$=appendix
+
+endif::basebackend-docbook[]
+
+ifdef::doctype-manpage[]
+(?i)^SYNOPSIS$=synopsis
+endif::doctype-manpage[]
diff --git a/doc/www/netatalk-relnotes.conf b/doc/www/netatalk-relnotes.conf
new file mode 100644 (file)
index 0000000..594ee2d
--- /dev/null
@@ -0,0 +1,285 @@
+#\r
+# netatalk.conf\r
+#\r
+# Asciidoc global configuration file.\r
+# css backend, generates XHTML 1.0 conformant markup.\r
+#\r
+# Included in css-embedded.conf\r
+#\r
+\r
+# Start with the html backend configuration.\r
+# include::html.conf[]\r
+\r
+[titles]\r
+underlines="--","==","~~","^^","++"\r
+\r
+[glossary]\r
+basebackend=css\r
+basebackend-css=\r
+basebackend-html\r
+dtddecl=PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"\r
+\r
+[tags]\r
+# Add title class.\r
+ilist={title?<p class="listtitle"><b>{title}</b></p>}<ul>|</ul>\r
+olist={title?<p class="listtitle"><b>{title}</b></p>}<ol>|</ol>\r
+vlist={title?<p class="listtitle"><b>{title}</b></p>}<dl>|</dl>\r
+qlist={title?<p class="listtitle"><b>{title}</b></p>}<ol>|</ol>\r
+\r
+[under-construction-blockmacro]\r
+<p class="under-construction">\r
+This page is currently under construction.<br/>\r
+Please return soon.\r
+</p>\r
+\r
+[image-inlinemacro]\r
+<a class="imagelink" href="{link}">\r
+# border="0" so broken IE6 does not put border round linked image.\r
+  <img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"} border="0"/>\r
+{link#}</a>\r
+\r
+[image-blockmacro]\r
+<div class="image">\r
+  <p>{link?<a class="imagelink" href="{link}">}\r
+# border="0" so broken IE6 does not put border round linked image.\r
+    <img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"} border="0"/>\r
+  {link?</a>}</p>\r
+  <p class="imagetitle"><b>Figure:</b> {title}</p>\r
+</div>\r
+\r
+# DEPRECATED.\r
+[graphic]\r
+<div class="graphic">\r
+  <p><img src="{target}" alt="{caption={target}}"/></p>\r
+  <p class="graphictitle"><b>Figure:</b> {title}</p>\r
+</div>\r
+\r
+# Paragraph substitution.\r
+[indentedparagraph]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+#<div class="indentedparagraph"><pre>|</pre></div>\r
+&nbsp;&nbsp;&nbsp;<a href="|">|</a>\r
+\r
+# Delimited block substitution.\r
+[indentedblock]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+<div class="indentedblock"><pre>\r
+|\r
+</pre></div>\r
+\r
+[verbatimblock]\r
+<p class="blocktitle"><b>Example:</b> {title}</p>\r
+<div class="verbatimblock"><pre>\r
+|\r
+</pre></div>\r
+\r
+[sidebarblock]\r
+<div class="sidebarblock">\r
+<p class="sidebartitle">{title}</p>\r
+|\r
+</div>\r
+\r
+[customblock]\r
+|\r
+\r
+[table]\r
+# Table captions not used because IE6 is broken.\r
+<p class="tabletitle"><b>Table:</b> {title}</p>\r
+# If you want styled table borders in IE use the following table tag:\r
+# 1. Border style specified here rather than in CSS because IE6 is broken.\r
+# 2. bordercolor attribute is IE specific and not valid XHTML 1.0.\r
+#<table rules="groups" border="2" bordercolor="green" frame="hsides"\r
+#      cellspacing="0" cellpadding="4">\r
+#\r
+# Use this in preference to be strictly XHTML 1.0 conformant.\r
+<table rules="groups" frame="{noborders?void}{noborders!hsides}" cellspacing="0" cellpadding="4">\r
+{headrows#}<thead{noborders? style="border-width: 0;"}>\r
+{headrows}\r
+{headrows#}</thead>\r
+{footrows#}<tfoot{noborders? style="border-width: 0;"}>\r
+{footrows}\r
+{footrows#}</tfoot>\r
+<tbody{noborders? style="border-width: 0;"}>\r
+{bodyrows}\r
+</tbody>\r
+</table>\r
+\r
+#-------------------------\r
+# article and book document types\r
+# Both use the article.css stylesheet\r
+#-------------------------\r
+ifndef::doctype-manpage[]\r
+\r
+[header]\r
+<!DOCTYPE html {dtddecl}>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<meta name="description" content="Netatalk - Unix file and print services for Apple clients" />\r
+<meta name="keywords" content="Netatalk, AFP, AFP Server, File Server, PAP, Print Server, Appletalk, Mac, OSX, OS X, OS9, OS 9" />\r
+<meta name="language" content="EN" />\r
+<meta name="publisher" content="netatalk.sourceforge.net" />\r
+<meta name="robots" content="Follow" />\r
+<link rel="stylesheet" type="text/css" charset="iso-8859-1" href="/css/site.css" />\r
+<link rel="stylesheet" type="text/css" charset="iso-8859-1" href="/css/printer.css" media="print" />\r
+<link rel="alternate stylesheet" type="text/css" charset="iso-8859-1" href="/css/printer.css" title="Printer" />\r
+<link rel="copyright" title="GNU General Public License" href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt" />\r
+<link rel="author" title="The Netatalk Development Team" href="http://netatalk.sf.net" />\r
+<link rel="home" href="index.php" title="Netatalk Home" />\r
+<link rel="home" href="http://www.sourceforge.net/projects/netatalk" title="Netatalk Sourceforge" />\r
+<link rel="bookmark" href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Downloads" />\r
+\r
+<title>Netatalk Release Notes</title>\r
+</head>\r
+<body>\r
+<div id="header">\r
+<div id="logo"></div>\r
+<div id="menlinks">\r
+        <a href="/" title="Return to Netatalk home">[main]</a>\r
+        <a href="https://sourceforge.net/apps/mediawiki/netatalk/index.php?title=Main_Page" title="Netatalk Wiki">[wiki]</a>\r
+        <a href="/3.0/htmldocs" title="Netatalk Manual">[documentation]</a>\r
+        <a href="http://sourceforge.net/project/showfiles.php?group_id=8642" title="Download Netatalk from sourceforge">[downloads]</a>\r
+        <a href="/support.php" title="Support">[support]</a>\r
+        <a href="/links.php" title="Netatalk related links">[links]</a>\r
+        <img src="/gfx/end.gif" alt="" width="125" height="7" />\r
+</div>\r
+</div>\r
+<div id="header-print">\r
+<h1>netatalk.sourceforge.net</h1>\r
+</div>\r
+<div class="search">\r
+<h4> search netatalk.sf.net</h4>\r
+<form method="get" action="http://www.google.com/search">\r
+<p>\r
+<input type="text" name="q" size="10" maxlength="255" value="" />\r
+<input type="hidden" name="hl" value="de" />\r
+<input type="hidden" name="sitesearch" value="netatalk.sourceforge.net" />\r
+<input type="submit" name="btnG" value="Go" />\r
+</p>\r
+</form>\r
+<span class="italic">powered by Google</span>\r
+</div>\r
+<div id="content">\r
+<h1>{doctitle}</h1>\r
+\r
+[footer]\r
+</div>\r
+<div class="footer">\r
+<span class="italic">webspace sponsored by</span><br />\r
+<!-- use a table for now -->\r
+<table>\r
+<tr>\r
+<td><a href="http://www.sf.net"><img src="http://sourceforge.net/sflogo.php?group_id=8642&amp;type=1" style="border:0;width:88px;height:31px" width="88" height="31" alt="SourceForge.net Logo" /></a></td>\r
+<td><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></td>\r
+<td><a href="http://jigsaw.w3.org/css-validator/"><img style="border:0;width:88px;height:31px" src="http://jigsaw.w3.org/css-validator/images/vcss" alt="Valid CSS!" /></a></td>\r
+</tr>\r
+</table>\r
+</div>\r
+</body>\r
+</html>\r
+\r
+[preamble]\r
+# Untitled elements between header and first section title.\r
+<div id="body">\r
+|\r
+</div>\r
+\r
+[sect0]\r
+<h2 class="sect0">{1?<a name="{1}"></a>}{title}</h2>\r
+|\r
+\r
+[sect1]\r
+<h2>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h2>\r
+|\r
+\r
+[sect2]\r
+<h3>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h3>\r
+|\r
+\r
+[sect3]\r
+<h4>{1?<a name="{1}"></a>}{section-numbers?{sectnum} }{title}</h4>\r
+|\r
+\r
+[sect4]\r
+<h5>{1?<a name="{1}"></a>}{title}</h5>\r
+|\r
+\r
+endif::doctype-manpage[]\r
+\r
+#-------------------------\r
+# manpage document type\r
+#-------------------------\r
+ifdef::doctype-manpage[]\r
+\r
+[header]\r
+<!DOCTYPE html {dtddecl}>\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />\r
+<meta name="generator" content="AsciiDoc {asciidoc-version}" />\r
+<meta name="author" content="{author}" />\r
+<meta name="author-email" content="{email}" />\r
+<link rel="stylesheet" href="manpage.css" type="text/css" />\r
+<title>{mantitle}</title>\r
+</head>\r
+<body>\r
+<div id="content">\r
+<h1>{doctitle} Manual Page</h1>\r
+<h2>NAME</h2>\r
+<p>{manname} -\r
+   {manpurpose}\r
+</p>\r
+\r
+[footer]\r
+<div id="footer">\r
+<p>\r
+Version {revision}<br/>\r
+Last updated {localdate} {localtime}\r
+</p>\r
+<p><span class="ahem">This document might look funny (or very plain) to you, since you're not using a browser which (correctly) supports CSS.</span></p>\r
+<div id="badges">\r
+<table border="0" cellpadding="8" summary="Badges">\r
+<tr>\r
+<td><a href="http://validator.w3.org/check/referer"><img\r
+# Source badge locally.\r
+#src="http://www.w3.org/Icons/valid-xhtml10"\r
+src="valid-xhtml10.png"\r
+alt="Valid XHTML 1.0" height="31" width="88" /></a></td>\r
+\r
+<td><a href="http://jigsaw.w3.org/css-validator/"> <img border="0"\r
+# Source badge locally.\r
+#src="http://jigsaw.w3.org/css-validator/images/vcss"\r
+src="vcss.png"\r
+alt="Valid CSS" width="88" height="31" /></a></td>\r
+</tr>\r
+</table>\r
+</div>\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
+\r
+# Section macros\r
+[sect-synopsis]\r
+<div id="synopsis">\r
+<h2>SYNOPSIS</h2>\r
+|\r
+</div>\r
+\r
+[sect1]\r
+<h2>{1?<a name="{1}"></a>}{title}</h2>\r
+|\r
+\r
+[sect2]\r
+<h3>{1?<a name="{1}"></a>}{title}</h3>\r
+|\r
+\r
+[sect3]\r
+<h4>{1?<a name="{1}"></a>}{title}</h4>\r
+|\r
+\r
+[sect4]\r
+<h5>{1?<a name="{1}"></a>}{title}</h5>\r
+|\r
+\r
+endif::doctype-manpage[]\r
diff --git a/doc/www/stylesheets/asciidoc.css b/doc/www/stylesheets/asciidoc.css
new file mode 100644 (file)
index 0000000..1475be7
--- /dev/null
@@ -0,0 +1,508 @@
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
+
+/* Default font. */
+body {
+  font-family: Georgia,serif;
+}
+
+/* Title font. */
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+  font-family: Arial,Helvetica,sans-serif;
+}
+
+body {
+  margin: 1em 5% 1em 5%;
+}
+
+a {
+  color: blue;
+  text-decoration: underline;
+}
+a:visited {
+  color: fuchsia;
+}
+
+em {
+  font-style: italic;
+  color: navy;
+}
+
+strong {
+  font-weight: bold;
+  color: #083194;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  color: #527bbd;
+  margin-top: 1.2em;
+  margin-bottom: 0.5em;
+  line-height: 1.3;
+}
+
+h1, h2, h3 {
+  border-bottom: 2px solid silver;
+}
+h2 {
+  padding-top: 0.5em;
+}
+h3 {
+  float: left;
+}
+h3 + * {
+  clear: left;
+}
+h5 {
+  font-size: 1.0em;
+}
+
+div.sectionbody {
+  margin-left: 0;
+}
+
+hr {
+  border: 1px solid silver;
+}
+
+p {
+  margin-top: 0.5em;
+  margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+  margin-top: 0;
+}
+ul > li     { color: #aaa; }
+ul > li > * { color: black; }
+
+pre {
+  padding: 0;
+  margin: 0;
+}
+
+#author {
+  color: #527bbd;
+  font-weight: bold;
+  font-size: 1.1em;
+}
+#email {
+}
+#revnumber, #revdate, #revremark {
+}
+
+#footer {
+  font-size: small;
+  border-top: 2px solid silver;
+  padding-top: 0.5em;
+  margin-top: 4.0em;
+}
+#footer-text {
+  float: left;
+  padding-bottom: 0.5em;
+}
+#footer-badges {
+  float: right;
+  padding-bottom: 0.5em;
+}
+
+#preamble {
+  margin-top: 1.5em;
+  margin-bottom: 1.5em;
+}
+div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+div.admonitionblock {
+  margin-top: 2.0em;
+  margin-bottom: 2.0em;
+  margin-right: 10%;
+  color: #606060;
+}
+
+div.content { /* Block element content. */
+  padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+  color: #527bbd;
+  font-weight: bold;
+  text-align: left;
+  margin-top: 1.0em;
+  margin-bottom: 0.5em;
+}
+div.title + * {
+  margin-top: 0;
+}
+
+td div.title:first-child {
+  margin-top: 0.0em;
+}
+div.content div.title:first-child {
+  margin-top: 0.0em;
+}
+div.content + div.title {
+  margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+  background: #ffffee;
+  border: 1px solid #dddddd;
+  border-left: 4px solid #f0f0f0;
+  padding: 0.5em;
+}
+
+div.listingblock > div.content {
+  border: 1px solid #dddddd;
+  border-left: 5px solid #f0f0f0;
+  background: #f8f8f8;
+  padding: 0.5em;
+}
+
+div.quoteblock, div.verseblock {
+  padding-left: 1.0em;
+  margin-left: 1.0em;
+  margin-right: 10%;
+  border-left: 5px solid #f0f0f0;
+  color: #777777;
+}
+
+div.quoteblock > div.attribution {
+  padding-top: 0.5em;
+  text-align: right;
+}
+
+div.verseblock > pre.content {
+  font-family: inherit;
+  font-size: inherit;
+}
+div.verseblock > div.attribution {
+  padding-top: 0.75em;
+  text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+  text-align: left;
+}
+
+div.admonitionblock .icon {
+  vertical-align: top;
+  font-size: 1.1em;
+  font-weight: bold;
+  text-decoration: underline;
+  color: #527bbd;
+  padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+  padding-left: 0.5em;
+  border-left: 3px solid #dddddd;
+}
+
+div.exampleblock > div.content {
+  border-left: 3px solid #dddddd;
+  padding-left: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+  margin-top: 0.8em;
+  margin-bottom: 0.8em;
+}
+dt {
+  margin-top: 0.5em;
+  margin-bottom: 0;
+  font-style: normal;
+  color: navy;
+}
+dd > *:first-child {
+  margin-top: 0.1em;
+}
+
+ul, ol {
+    list-style-position: outside;
+}
+ol.arabic {
+  list-style-type: decimal;
+}
+ol.loweralpha {
+  list-style-type: lower-alpha;
+}
+ol.upperalpha {
+  list-style-type: upper-alpha;
+}
+ol.lowerroman {
+  list-style-type: lower-roman;
+}
+ol.upperroman {
+  list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+  margin-top: 0.1em;
+  margin-bottom: 0.1em;
+}
+
+tfoot {
+  font-weight: bold;
+}
+td > div.verse {
+  white-space: pre;
+}
+
+div.hdlist {
+  margin-top: 0.8em;
+  margin-bottom: 0.8em;
+}
+div.hdlist tr {
+  padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+  font-weight: bold;
+}
+td.hdlist1 {
+  vertical-align: top;
+  font-style: normal;
+  padding-right: 0.8em;
+  color: navy;
+}
+td.hdlist2 {
+  vertical-align: top;
+}
+div.hdlist.compact tr {
+  margin: 0;
+  padding-bottom: 0;
+}
+
+.comment {
+  background: yellow;
+}
+
+.footnote, .footnoteref {
+  font-size: 0.8em;
+}
+
+span.footnote, span.footnoteref {
+  vertical-align: super;
+}
+
+#footnotes {
+  margin: 20px 0 20px 0;
+  padding: 7px 0 0 0;
+}
+
+#footnotes div.footnote {
+  margin: 0 0 5px 0;
+}
+
+#footnotes hr {
+  border: none;
+  border-top: 1px solid silver;
+  height: 1px;
+  text-align: left;
+  margin-left: 0;
+  width: 20%;
+  min-width: 100px;
+}
+
+div.colist td {
+  padding-right: 0.5em;
+  padding-bottom: 0.3em;
+  vertical-align: top;
+}
+div.colist td img {
+  margin-top: 0.3em;
+}
+
+@media print {
+  #footer-badges { display: none; }
+}
+
+#toc {
+  margin-bottom: 2.5em;
+}
+
+#toctitle {
+  color: #527bbd;
+  font-size: 1.1em;
+  font-weight: bold;
+  margin-top: 1.0em;
+  margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+div.toclevel2 {
+  margin-left: 2em;
+  font-size: 0.9em;
+}
+div.toclevel3 {
+  margin-left: 4em;
+  font-size: 0.9em;
+}
+div.toclevel4 {
+  margin-left: 6em;
+  font-size: 0.9em;
+}
+
+span.aqua { color: aqua; }
+span.black { color: black; }
+span.blue { color: blue; }
+span.fuchsia { color: fuchsia; }
+span.gray { color: gray; }
+span.green { color: green; }
+span.lime { color: lime; }
+span.maroon { color: maroon; }
+span.navy { color: navy; }
+span.olive { color: olive; }
+span.purple { color: purple; }
+span.red { color: red; }
+span.silver { color: silver; }
+span.teal { color: teal; }
+span.white { color: white; }
+span.yellow { color: yellow; }
+
+span.aqua-background { background: aqua; }
+span.black-background { background: black; }
+span.blue-background { background: blue; }
+span.fuchsia-background { background: fuchsia; }
+span.gray-background { background: gray; }
+span.green-background { background: green; }
+span.lime-background { background: lime; }
+span.maroon-background { background: maroon; }
+span.navy-background { background: navy; }
+span.olive-background { background: olive; }
+span.purple-background { background: purple; }
+span.red-background { background: red; }
+span.silver-background { background: silver; }
+span.teal-background { background: teal; }
+span.white-background { background: white; }
+span.yellow-background { background: yellow; }
+
+span.big { font-size: 2em; }
+span.small { font-size: 0.6em; }
+
+span.underline { text-decoration: underline; }
+span.overline { text-decoration: overline; }
+span.line-through { text-decoration: line-through; }
+
+
+/*
+ * xhtml11 specific
+ *
+ * */
+
+tt {
+  font-family: monospace;
+  font-size: inherit;
+  color: navy;
+}
+
+div.tableblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+div.tableblock > table {
+  border: 3px solid #527bbd;
+}
+thead, p.table.header {
+  font-weight: bold;
+  color: #527bbd;
+}
+p.table {
+  margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+  border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+  border-left-style: none;
+  border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+  border-top-style: none;
+  border-bottom-style: none;
+}
+
+
+/*
+ * html5 specific
+ *
+ * */
+
+.monospaced {
+  font-family: monospace;
+  font-size: inherit;
+  color: navy;
+}
+
+table.tableblock {
+  margin-top: 1.0em;
+  margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+  font-weight: bold;
+  color: #527bbd;
+}
+p.tableblock {
+  margin-top: 0;
+}
+table.tableblock {
+  border-width: 3px;
+  border-spacing: 0px;
+  border-style: solid;
+  border-color: #527bbd;
+  border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+  border-width: 1px;
+  padding: 4px;
+  border-style: solid;
+  border-color: #527bbd;
+}
+
+table.tableblock.frame-topbot {
+  border-left-style: hidden;
+  border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+  border-top-style: hidden;
+  border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+  border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+  text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+  text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+  text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+  vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+  vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+  vertical-align: bottom;
+}
index 85b51dc86bdca8971ecb85ce94c66c1273c28e21..ddff6c3e42d8057d800ef9bac6ec888b529db58c 100644 (file)
@@ -1,6 +1,10 @@
 # Makefile.am for etc/afpd/
 
 pkgconfdir = @PKGCONFDIR@
+BUILT_SOURCES =
+EXTRA_DIST = afpstats-service.xml afpstats_service_glue.h
+CLEANFILES =
+DISTCLEANFILES =
 
 sbin_PROGRAMS = afpd
 noinst_PROGRAMS = hash fce
@@ -28,7 +32,6 @@ afpd_SOURCES = \
        file.c \
        filedir.c \
        fork.c \
-       gettok.c \
        hash.c \
        main.c \
        mangle.c \
@@ -43,6 +46,7 @@ afpd_SOURCES = \
        unix.c \
        volume.c
 
+
 afpd_LDADD =  \
        $(top_builddir)/libatalk/libatalk.la \
        @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@
@@ -50,7 +54,7 @@ afpd_LDADD =  \
 afpd_LDFLAGS = -export-dynamic
 
 afpd_CFLAGS = \
-       @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@\
+       @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
        -DAPPLCNAME \
        -DSERVERTEXT=\"$(SERVERTEXT)/\" \
        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
@@ -62,11 +66,36 @@ if HAVE_ACLS
 afpd_SOURCES += acls.c
 endif
 
+if HAVE_DBUS_GLIB
+BUILT_SOURCES += afpstats_service_glue.h
+DISTCLEANFILES += afpstats_service_glue.h
+
+afpstats_service_glue.h: afpstats-service.xml
+       $(LIBTOOL) --mode=execute \
+               dbus-binding-tool \
+                       --prefix=afpstats_obj \
+                       --mode=glib-server \
+                       --output=afpstats_service_glue.h \
+                       $(top_srcdir)/etc/afpd/afpstats-service.xml
+
+afpd_SOURCES += afpstats.c afpstats_obj.c
+afpd_CFLAGS  += $(DBUS_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GTHREAD_CFLAGS) -DDBUS_COMPILATION
+afpd_LDFLAGS += $(DBUS_LIBS) $(DBUS_GLIB_LIBS) $(DBUS_GTHREAD_LIBS) -ldbus-glib-1
+endif
+
+if WITH_DTRACE
+DTRACE_OBJ = afpd-afp_dsi.o afpd-fork.o afpd-appl.o afpd-catsearch.o afpd-directory.o afpd-enumerate.o afpd-file.o afpd-filedir.o
+afp_dtrace.o: $(top_srcdir)/include/atalk/afp_dtrace.d $(DTRACE_OBJ)
+       if test -f afp_dtrace.o ; then rm -f afp_dtrace.o ; fi
+       $(LIBTOOL) --mode=execute dtrace -G -s $(top_srcdir)/include/atalk/afp_dtrace.d -o afp_dtrace.o $(DTRACE_OBJ)
+afpd_LDADD += afp_dtrace.o @DTRACE_LIBS@
+CLEANFILES += afp_dtrace.o
+endif
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
         filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
         uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
-        dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h
+        dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h afpstats.h afpstats_obj.h
 
 hash_SOURCES = hash.c
 hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
index 305ef06fed6c65a7cf15e6dbe8e50f325f3f8736..d162b994e7af71d09436f0b937d80a2cd2b3bffa 100644 (file)
  *
  * @param obj            (r) handle
  * @param path           (r) path to filesystem object
- * @param sb             (r) struct stat of path
- * @param result         (w) resulting Darwin allow ACE
+ * @param sb             (rw) struct stat of path
+ * @param ma             (rw) UARights struct
+ * @param rights_out     (w) mapped Darwin ACL rights
  *
  * @returns                  0 or -1 on error
  */
 static int solaris_acl_rights(const AFPObj *obj,
                               const char *path,
-                              const struct stat *sb,
-                              uint32_t *result)
+                              struct stat *sb,
+                              struct maccess *ma,
+                              uint32_t *rights_out)
 {
     EC_INIT;
     int      i, ace_count, checkgroup;
@@ -168,7 +170,28 @@ static int solaris_acl_rights(const AFPObj *obj,
             darwin_rights |= nfsv4_to_darwin_rights[i].to;
     }
 
-    *result |= darwin_rights;
+    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", darwin_rights);
+
+    if (rights_out)
+        *rights_out = darwin_rights;
+
+    if (ma && obj->options.flags & OPTION_ACL2MACCESS) {
+        if (darwin_rights & DARWIN_ACE_READ_DATA)
+            ma->ma_user |= AR_UREAD;
+        if (darwin_rights & DARWIN_ACE_WRITE_DATA)
+            ma->ma_user |= AR_UWRITE;
+        if (darwin_rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
+            ma->ma_user |= AR_USEARCH;
+    }
+
+    if (sb && obj->options.flags & OPTION_ACL2MODE) {
+        if (darwin_rights & DARWIN_ACE_READ_DATA)
+            sb->st_mode |= S_IRUSR;
+        if (darwin_rights & DARWIN_ACE_WRITE_DATA)
+            sb->st_mode |= S_IWUSR;
+        if (darwin_rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
+            sb->st_mode |= S_IXUSR;
+    }
 
 EC_CLEANUP:
     if (aces) free(aces);
@@ -607,21 +630,23 @@ static int posix_acls_to_uaperms(const AFPObj *obj, const char *path, struct sta
                 break;
         }
     }
-    /* apply the mask and adjust user and group permissions */
-    ma->ma_user |= (acl_rights & mask);
-    ma->ma_group = (group_rights & mask);
 
-    /* update st_mode to properly reflect group permissions */
-    sb->st_mode &= ~S_IRWXG;
-
-    if (ma->ma_group & AR_USEARCH)
-        sb->st_mode |= S_IXGRP;
-
-    if (ma->ma_group & AR_UWRITE)
-        sb->st_mode |= S_IWGRP;
+    if (obj->options.flags & OPTION_ACL2MACCESS) {
+        /* apply the mask and adjust user and group permissions */
+        ma->ma_user |= (acl_rights & mask);
+        ma->ma_group = (group_rights & mask);
+    }
 
-    if (ma->ma_group & AR_UREAD)
-        sb->st_mode |= S_IRGRP;
+    if (obj->options.flags & OPTION_ACL2MODE) {
+        /* update st_mode to properly reflect group permissions */
+        sb->st_mode &= ~S_IRWXG;
+        if (ma->ma_group & AR_USEARCH)
+            sb->st_mode |= S_IXGRP;
+        if (ma->ma_group & AR_UWRITE)
+            sb->st_mode |= S_IWGRP;
+        if (ma->ma_group & AR_UREAD)
+            sb->st_mode |= S_IRGRP;
+    }
 
 EC_CLEANUP:
     if (acl) acl_free(acl);
@@ -629,6 +654,7 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
+#if 0
 /*!
  * Add entries of one acl to another acl
  *
@@ -651,6 +677,7 @@ static int acl_add_acl(acl_t *aclp, const acl_t acl)
 EC_CLEANUP:
     EC_EXIT;
 }
+#endif
 
 /*!
  * Map Darwin ACE rights to POSIX 1e perm
@@ -1155,17 +1182,21 @@ static int set_acl(const struct vol *vol,
     }
     LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
 
-    /* Ressourcefork first.
-       Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
-       might be strange for ACE_DELETE_CHILD and for inheritance flags. */
+    /* Ressourcefork first */
     if ((ret = (vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
-        LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
-        if (errno == (EACCES | EPERM))
+        LOG(log_debug, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+        switch (errno) {
+        case EACCES:
+        case EPERM:
             EC_STATUS(AFPERR_ACCESS);
-        else if (errno == ENOENT)
-            EC_STATUS(AFPERR_NOITEM);
-        else
+            break;
+        case ENOENT:
+            EC_STATUS(AFP_OK);
+            break;
+        default:
             EC_STATUS(AFPERR_MISC);
+            break;
+        }
         goto EC_CLEANUP;
     }
     if ((ret = (acl(name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
@@ -1261,7 +1292,6 @@ static int set_acl(const struct vol *vol,
     acl_entry_t entry;
     acl_tag_t tag;
     int entry_id = ACL_FIRST_ENTRY;
-    int has_def_acl = 0;
     /* flags to indicate if the object has a minimal default acl and/or an extended
      * default acl.
      */
@@ -1355,7 +1385,6 @@ static int check_acl_access(const AFPObj *obj,
     int            ret;
     uint32_t       allowed_rights = 0;
     char           *username = NULL;
-    uuidtype_t     uuidtype;
     struct stat    st;
     bstring        parent = NULL;
     int            is_dir;
@@ -1363,6 +1392,8 @@ static int check_acl_access(const AFPObj *obj,
     LOG(log_maxdebug, logtype_afpd, "check_acl_access(dir: \"%s\", path: \"%s\", curdir: \"%s\", 0x%08x)",
         cfrombstr(dir->d_fullpath), path, getcwdpath(), requested_rights);
 
+    AFP_ASSERT(vol);
+
     /* This check is not used anymore, as OS X Server seems to be ignoring too */
 #if 0
     /* Get uid or gid from UUID */
@@ -1380,7 +1411,7 @@ static int check_acl_access(const AFPObj *obj,
     }
 #endif
 
-    EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
+    EC_ZERO_LOG_ERR(ostat(path, &st, vol_syml_opt(vol)), AFPERR_PARAM);
 
     is_dir = !strcmp(path, ".");
 
@@ -1390,7 +1421,7 @@ static int check_acl_access(const AFPObj *obj,
         LOG(log_debug, logtype_afpd, "check_access: allowed rights from dircache: 0x%08x", allowed_rights);
     } else {
 #ifdef HAVE_SOLARIS_ACLS
-        EC_ZERO_LOG(solaris_acl_rights(obj, path, &st, &allowed_rights));
+        EC_ZERO_LOG(solaris_acl_rights(obj, path, &st, NULL, &allowed_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
         EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &allowed_rights));
@@ -1423,7 +1454,7 @@ static int check_acl_access(const AFPObj *obj,
             EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
 
 #ifdef HAVE_SOLARIS_ACLS
-            EC_ZERO_LOG(solaris_acl_rights(obj, cfrombstr(parent), &st, &parent_rights));
+            EC_ZERO_LOG(solaris_acl_rights(obj, cfrombstr(parent), &st, NULL, &parent_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
             EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &parent_rights));
@@ -1577,10 +1608,10 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
         if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
             LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
-            localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
+            localuuid_from_id((unsigned char *)rbuf, UUID_USER, s_path->st.st_uid);
         } else {
             LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
-            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, (unsigned char *)rbuf)) != 0)
                 return AFPERR_MISC;
         }
         rbuf += UUID_BINSIZE;
@@ -1592,10 +1623,10 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
         if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
             LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
-            localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
+            localuuid_from_id((unsigned char *)rbuf, UUID_GROUP, s_path->st.st_gid);
         } else {
             LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
-            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, (unsigned char *)rbuf)) != 0)
                 return AFPERR_MISC;
         }
         rbuf += UUID_BINSIZE;
@@ -1727,9 +1758,8 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 int acltoownermode(const AFPObj *obj, const struct vol *vol, char *path, struct stat *st, struct maccess *ma)
 {
     EC_INIT;
-    uint32_t rights = 0;
 
-    if ( ! (obj->options.flags & OPTION_ACL2MACCESS)
+    if ( ! (obj->options.flags & (OPTION_ACL2MACCESS | OPTION_ACL2MODE))
          || ! (vol->v_flags & AFPVOL_ACLS))
          return 0;
 
@@ -1737,16 +1767,7 @@ int acltoownermode(const AFPObj *obj, const struct vol *vol, char *path, struct
         getcwdpath(), path, ma->ma_user);
 
 #ifdef HAVE_SOLARIS_ACLS
-    EC_ZERO_LOG(solaris_acl_rights(obj, path, st, &rights));
-
-    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
-
-    if (rights & DARWIN_ACE_READ_DATA)
-        ma->ma_user |= AR_UREAD;
-    if (rights & DARWIN_ACE_WRITE_DATA)
-        ma->ma_user |= AR_UWRITE;
-    if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
-        ma->ma_user |= AR_USEARCH;
+    EC_ZERO_LOG(solaris_acl_rights(obj, path, st, ma, NULL));
 #endif
 
 #ifdef HAVE_POSIX_ACLS
index a61fe1022f551ccc84b9fef03c860cfe16c51a30..5a5aaa35df9e6bd1cf51fe13d66368e41a44ab3a 100644 (file)
@@ -17,6 +17,9 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
 
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include "volume.h"
 #include "afp_zeroconf.h"
 
-
 /*!
- * Free and cleanup all linked DSI objects from config
+ * Free and cleanup config and DSI
  *
- * Preserve object pointed to by "dsi".
- * "dsi" can be NULL in which case all DSI objects _and_ the options object are freed 
+ * "dsi" can be NULL in which case all DSI objects and the config object is freed,
+ * otherwise its an afpd session child and only any unneeded DSI objects are freed
  */
 void configfree(AFPObj *obj, DSI *dsi)
 {
     DSI *p, *q;
 
-    /* the master loaded the volumes for zeroconf, get rid of that */
+    if (!dsi) {
+        /* Master afpd reloading config */
+        auth_unload();
+        if (! (obj->options.flags & OPTION_NOZEROCONF)) {
+            zeroconf_deregister();
+        }
+    }
+
     unload_volumes(obj);
 
+    /* Master and child releasing unneeded DSI handles */
     for (p = obj->dsi; p; p = q) {
         q = p->next;
         if (p == dsi)
             continue;
-        close(p->socket);
+        dsi_free(p);
         free(p);
     }
+    obj->dsi = NULL;
 
+    /* afpd session child passes dsi handle to obj handle */
     if (dsi) {
         dsi->next = NULL;
         obj->dsi = dsi;
-    } else {
-        afp_options_free(&obj->options);
     }
 }
 
@@ -75,30 +85,113 @@ void configfree(AFPObj *obj, DSI *dsi)
 int configinit(AFPObj *obj)
 {
     EC_INIT;
-    DSI *dsi, **next = &obj->dsi;
+    DSI *dsi = NULL;
+    DSI **next = &obj->dsi;
     char *p = NULL, *q = NULL, *savep;
     const char *r;
+    struct ifaddrs *ifaddr, *ifa;
+    int family, s;
+    static char interfaddr[NI_MAXHOST];
 
     auth_load(obj->options.uampath, obj->options.uamlist);
     set_signature(&obj->options);
+#ifdef HAVE_LDAP
+    acl_ldap_freeconfig();
+#endif /* HAVE_LDAP */
 
-    LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, port: %s",
+    LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, interfaces: %s, port: %s",
         obj->options.hostname,
-        obj->options.listen ? obj->options.listen : "(default: hostname)",
+        obj->options.listen ? obj->options.listen : "-",
+        obj->options.interfaces ? obj->options.interfaces : "-",
         obj->options.port);
 
-    /* obj->options->listen is of the from "IP[:port][,IP:[PORT], ...]" */
-    /* obj->options->port is the default port to listen (548) */
-
+    /*
+     * Setup addresses we listen on from hostname and/or "afp listen" option
+     */
     if (obj->options.listen) {
         EC_NULL( q = p = strdup(obj->options.listen) );
         EC_NULL( p = strtok_r(p, ", ", &savep) );
+        while (p) {
+            if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL)
+                break;
+
+            status_init(obj, dsi);
+            *next = dsi;
+            next = &dsi->next;
+            dsi->AFPobj = obj;
+
+            LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
+                getip_string((struct sockaddr *)&dsi->server),
+                getip_port((struct sockaddr *)&dsi->server));
+
+            p = strtok_r(NULL, ", ", &savep);
+        }
+        if (q) {
+            free(q);
+            q = NULL;
+        }
     }
 
-    while (1) {
-        if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL)
-            break;
+   /*
+    * Setup addresses we listen on from "afp interfaces".
+    * We use getifaddrs() instead of if_nameindex() because the latter appears still
+    * to be unable to return ipv4 addresses
+    */
+    if (obj->options.interfaces) {
+#ifndef HAVE_GETIFADDRS
+        LOG(log_error, logtype_afpd, "option \"afp interfaces\" not supported");
+#else
+        if (getifaddrs(&ifaddr) == -1) {
+            LOG(log_error, logtype_afpd, "getinterfaddr: getifaddrs() failed: %s", strerror(errno));
+            EC_FAIL;
+        }
+
+        EC_NULL( q = p = strdup(obj->options.interfaces) );
+        EC_NULL( p = strtok_r(p, ", ", &savep) );
+        while (p) {
+            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+                if (ifa->ifa_addr == NULL)
+                    continue;
+                if (STRCMP(ifa->ifa_name, !=, p))
+                    continue;
+
+                family = ifa->ifa_addr->sa_family;
+                if (family == AF_INET || family == AF_INET6) {
+                    if (getnameinfo(ifa->ifa_addr,
+                                    (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
+                                    interfaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
+                        LOG(log_error, logtype_afpd, "getinterfaddr: getnameinfo() failed %s", gai_strerror(errno));
+                        continue;
+                    }
+
+                    if ((dsi = dsi_init(obj, obj->options.hostname, interfaddr, obj->options.port)) == NULL)
+                        continue;
+
+                    status_init(obj, dsi);
+                    *next = dsi;
+                    next = &dsi->next;
+                    dsi->AFPobj = obj;
+
+                    LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on interface %s with address %s:%d",
+                        p,
+                        getip_string((struct sockaddr *)&dsi->server),
+                        getip_port((struct sockaddr *)&dsi->server));
+                } /* if (family == AF_INET || family == AF_INET6) */
+            } /* for (ifa != NULL) */
+            p = strtok_r(NULL, ", ", &savep);
+        }
+        freeifaddrs(ifaddr);
+#endif
+    }
 
+    /*
+     * Check whether we got a valid DSI from options.listen or options.interfaces,
+     * if not add a DSI that accepts all connections and goes though the list of
+     * network interaces for determining an IP we can advertise in DSIStatus
+     */
+    if (dsi == NULL) {
+        if ((dsi = dsi_init(obj, obj->options.hostname, NULL, obj->options.port)) == NULL)
+            EC_FAIL_LOG("no suitable network address found, use \"afp listen\" or \"afp interfaces\"", 0);
         status_init(obj, dsi);
         *next = dsi;
         next = &dsi->next;
@@ -107,12 +200,6 @@ int configinit(AFPObj *obj)
         LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
             getip_string((struct sockaddr *)&dsi->server),
             getip_port((struct sockaddr *)&dsi->server));
-
-        if (p)
-            /* p is NULL if ! obj->options.listen */
-            p = strtok_r(NULL, ", ", &savep);
-        if (!p)
-            break;
     }
 
 #ifdef HAVE_LDAP
@@ -122,7 +209,7 @@ int configinit(AFPObj *obj)
 
     /* Now register with zeroconf, we also need the volumes for that */
     if (! (obj->options.flags & OPTION_NOZEROCONF)) {
-        load_volumes(obj, NULL);
+        load_volumes(obj);
         zeroconf_register(obj);
     }
 
index 3e5cd47c3cbf4de4438bc6e3e0e2585a9be72417..5082ee70fea0f83d6d528fcc49a875520376d99c 100644 (file)
@@ -140,25 +140,6 @@ static void afp_dsi_die(int sig)
     }
 }
 
-/* SIGQUIT handler */
-static void ipc_reconnect_handler(int sig _U_)
-{
-    DSI *dsi = (DSI *)AFPobj->dsi;
-
-    if (reconnect_ipc(AFPobj) != 0) {
-        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC reconnect");
-        afp_dsi_close(AFPobj);
-        exit(EXITERR_SYS);        
-    }
-
-    if (ipc_child_write(AFPobj->ipc_fd, IPC_GETSESSION, AFPobj->sinfo.clientid_len, AFPobj->sinfo.clientid) != 0) {
-        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC ID resend");
-        afp_dsi_close(AFPobj);
-        exit(EXITERR_SYS);        
-    }
-    LOG(log_note, logtype_afpd, "ipc_reconnect_handler: IPC reconnect done");
-}
-
 /* SIGURG handler (primary reconnect) */
 static void afp_dsi_transfer_session(int sig _U_)
 {
@@ -409,7 +390,7 @@ void afp_over_dsi_sighandlers(AFPObj *obj)
     }
 
     /* install SIGQUIT */
-    action.sa_handler = ipc_reconnect_handler;
+    action.sa_handler = afp_dsi_die;
     if ( sigaction(SIGQUIT, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
@@ -492,6 +473,8 @@ void afp_over_dsi(AFPObj *obj)
     int flag = 1;
     setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
 
+    ipc_child_state(obj, DSI_RUNNING);
+
     /* get stuck here until the end */
     while (1) {
         if (sigsetjmp(recon_jmp, 1) != 0)
@@ -516,15 +499,6 @@ void afp_over_dsi(AFPObj *obj)
                 exit(0);
             }
 
-#if 0
-            /*  got ECONNRESET in read from client => exit*/
-            if (dsi->flags & DSI_GOT_ECONNRESET) {
-                LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
-                afp_dsi_close(obj);
-                exit(0);
-            }
-#endif
-
             if (dsi->flags & DSI_RECONINPROG) {
                 LOG(log_note, logtype_afpd, "afp_over_dsi: failed reconnect");
                 afp_dsi_close(obj);
@@ -535,8 +509,11 @@ void afp_over_dsi(AFPObj *obj)
             if (dsi_disconnect(dsi) != 0)
                 afp_dsi_die(EXITERR_CLNT);
 
+            ipc_child_state(obj, DSI_DISCONNECTED);
+
             while (dsi->flags & DSI_DISCONNECTED)
                 pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+            ipc_child_state(obj, DSI_RUNNING);
             continue; /* continue receiving until disconnect timer expires
                        * or a primary reconnect succeeds  */
         }
@@ -545,11 +522,12 @@ void afp_over_dsi(AFPObj *obj)
             LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
             dsi->flags &= ~DSI_SLEEPING;
             dsi->tickle = 0;
+            ipc_child_state(obj, DSI_RUNNING);
         }
 
         if (reload_request) {
             reload_request = 0;
-            load_volumes(AFPobj, closevol);
+            load_volumes(AFPobj);
         }
 
         /* The first SIGINT enables debugging, the next restores the config */
@@ -625,10 +603,12 @@ void afp_over_dsi(AFPObj *obj)
 
                     LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
+                    AFP_AFPFUNC_START(function, (char *)AfpNum2name(function));
                     err = (*afp_switch[function])(obj,
-                                                  dsi->commands, dsi->cmdlen,
+                                                  (char *)dsi->commands, dsi->cmdlen,
                                                   (char *)&dsi->data, &dsi->datalen);
 
+                    AFP_AFPFUNC_DONE(function, (char *)AfpNum2name(function));
                     LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                         AfpNum2name(function), AfpErr2name(err));
 
@@ -666,10 +646,14 @@ void afp_over_dsi(AFPObj *obj)
 
                 LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
+                AFP_AFPFUNC_START(function, (char *)AfpNum2name(function));
+
                 err = (*afp_switch[function])(obj,
-                                              dsi->commands, dsi->cmdlen,
+                                              (char *)dsi->commands, dsi->cmdlen,
                                               (char *)&dsi->data, &dsi->datalen);
 
+                AFP_AFPFUNC_DONE(function, (char *)AfpNum2name(function));
+
                 LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                     AfpNum2name(function), AfpErr2name(err));
 
index e14791fee2dd93c4db6b8deef1c65f92ba168b77..3158ce7b7fee9ef3058563309151defdc211b53b 100644 (file)
@@ -50,15 +50,16 @@ static pthread_t       poller;
         free(str); free(key);                           \
     }
 
+static struct pollfd *fds;
 
 /*
  * This is the thread that polls the filehandles
  */
-void *polling_thread(void *arg) {
+static void *polling_thread(void *arg) {
     // First we loop through getting the filehandles and adding them to our poll, we
     // need to allocate our pollfd's
     DNSServiceErrorType error;
-    struct pollfd           *fds = calloc(svc_ref_count, sizeof(struct pollfd));
+    fds = calloc(svc_ref_count, sizeof(struct pollfd));
     assert(fds);
 
     for(int i=0; i < svc_ref_count; i++) {
@@ -78,28 +79,32 @@ void *polling_thread(void *arg) {
     return(NULL);
 }
 
-
 /*
  * This is the callback for the service register function ... actually there isn't a lot
  * we can do if we get problems, so we don't really need to do anything other than report
  * the issue.
  */
-void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
-                   const char *name, const char *regtype, const char *domain, void *context) {
-
-    if(errorCode != kDNSServiceErr_NoError) {
+static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
+                          const char *name, const char *regtype, const char *domain, void *context)
+{
+    if (errorCode != kDNSServiceErr_NoError) {
         LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
             name, regtype, domain, errorCode);
     }
 }
 
-
 /*
  * This function unregisters anything we have already
  * registered and frees associated memory
  */
 static void unregister_stuff() {
-    pthread_kill(poller, SIGKILL);
+    pthread_cancel(poller);    
+
+    for (int i = 0; i < svc_ref_count; i++)
+        close(fds[i].fd);
+    free(fds);
+    fds = NULL;
+
     if(svc_refs) {
         for(int i=0; i < svc_ref_count; i++) {
             DNSServiceRefDeallocate(svc_refs[i]);
@@ -232,7 +237,7 @@ static void register_stuff(const AFPObj *obj) {
             LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
                 dsi->bonjourname, obj->options.mimicmodel);
             TXTRecordCreate(&txt_devinfo, 0, NULL);
-            TXTRecordPrintf(&txt_devinfo, "model=%s", obj->options.mimicmodel);
+            TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel);
             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
                                        0,               // no flags
                                        0,               // all network interfaces
@@ -240,7 +245,14 @@ static void register_stuff(const AFPObj *obj) {
                                        DEV_INFO_SERVICE_TYPE,
                                        "",            // default domains
                                        NULL,            // default host name
-                                       0,
+                                       /*
+                                        * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
+                                        *   "A value of 0 for a port is passed to register placeholder services.
+                                        *    Place holder services are not found  when browsing, but other
+                                        *    clients cannot register with the same name as the placeholder service."
+                                        * We therefor use port 9 which is used by the adisk service type.
+                                        */
+                                       htons(9),
                                        TXTRecordGetLength(&txt_devinfo),
                                        TXTRecordGetBytesPtr(&txt_devinfo),
                                        RegisterReply,           // callback
index 223f117881e20ca8b485290b4577ec6a999708d3..932e6a657ea5215faa3ca69f7ec371425d62f2ef 100644 (file)
 
 #define LENGTH 512
 
-/* get rid of any allocated afp_option buffers. */
-void afp_options_free(struct afp_options *opt)
-{
-       if (opt->hostname)
-        free(opt->hostname);
-       if (opt->adminauthuser)
-        free(opt->adminauthuser);
-       if (opt->configfile)
-        free(opt->configfile);
-    if (opt->fqdn)
-        free(opt->fqdn);
-    if (opt->guest)
-        free(opt->guest);
-    if (opt->listen)
-        free(opt->listen);
-    if (opt->k5realm)
-        free(opt->k5realm);
-    if (opt->k5keytab)
-        free(opt->k5keytab);
-    if (opt->k5service)
-        free(opt->k5service);
-    if (opt->logconfig)
-        free(opt->logconfig);
-    if (opt->logfile)
-        free(opt->logfile);
-    if (opt->loginmesg)
-        free(opt->loginmesg);
-    if (opt->maccodepage)
-        free(opt->maccodepage);
-       if (opt->mimicmodel)
-        free(opt->mimicmodel);
-    if (opt->ntdomain)
-        free(opt->ntdomain);
-    if (opt->ntseparator)
-        free(opt->ntseparator);
-    if (opt->passwdfile)
-        free(opt->passwdfile);
-    if (opt->port)
-        free(opt->port);
-    if (opt->signatureopt)
-        free(opt->signatureopt);
-    if (opt->uamlist)
-        free(opt->uamlist);
-    if (opt->uampath)
-        free(opt->uampath);
-    if (opt->unixcodepage)
-        free(opt->unixcodepage);
-}
-
 /*
  * Show version information about afpd.
  * Used by "afp -v".
@@ -214,6 +165,20 @@ static void show_version_extended(void )
 #else
        puts( "No" );
 #endif
+
+       printf( "         D-Bus support:\t" );
+#ifdef HAVE_DBUS_GLIB
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
+       printf( "         DTrace probes:\t" );
+#ifdef WITH_DTRACE
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
 }
 
 /*
@@ -222,6 +187,7 @@ static void show_version_extended(void )
 static void show_paths( void )
 {
        printf( "              afp.conf:\t%s\n", _PATH_CONFDIR "afp.conf");
+       printf( "           extmap.conf:\t%s\n", _PATH_CONFDIR "extmap.conf");
        printf( "       state directory:\t%s\n", _PATH_STATEDIR);
        printf( "    afp_signature.conf:\t%s\n", _PATH_STATEDIR "afp_signature.conf");
        printf( "      afp_voluuid.conf:\t%s\n", _PATH_STATEDIR "afp_voluuid.conf");
index a8282c22b3fb38e57a4ddb94af1e529004a7022f..b7bc00f8f51709f1673410cecf773605ca3a5876 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: afp_util.c,v 1.10 2010-01-23 14:44:42 franklahm Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
diff --git a/etc/afpd/afpstats-service.xml b/etc/afpd/afpstats-service.xml
new file mode 100644 (file)
index 0000000..099c778
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/org/netatalk/AFPStats">
+  <interface name="org.netatalk.AFPStats">
+    <method name="GetUsers">
+       <arg name="ret" type="as" direction="out"/>
+    </method>
+  </interface>
+</node>
diff --git a/etc/afpd/afpstats.c b/etc/afpd/afpstats.c
new file mode 100644 (file)
index 0000000..80d3e6f
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <atalk/logger.h>
+#include <atalk/compat.h>
+#include <atalk/errchk.h>
+#include <atalk/server_child.h>
+
+#include "afpstats_obj.h"
+#include "afpstats_service_glue.h"
+
+/*
+ * Beware: this struct is accessed and modified from the main thread
+ * and from this thread, thus be careful to lock and unlock the mutex.
+ */
+static server_child_t *childs;
+
+static gpointer afpstats_thread(gpointer _data)
+{
+    DBusGConnection *bus;
+    DBusGProxy *bus_proxy;
+    GError *error = NULL;
+    GMainContext *ctxt;
+    GMainLoop *thread_loop;
+    guint request_name_result;
+    sigset_t sigs;
+
+    /* Block all signals in this thread */
+    sigfillset(&sigs);
+    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+
+    ctxt = g_main_context_new();
+    thread_loop = g_main_loop_new(ctxt, FALSE);
+
+    dbus_g_object_type_install_info(AFPSTATS_TYPE_OBJECT, &dbus_glib_afpstats_obj_object_info);
+   
+    if (!(bus = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, ctxt, &error))) {
+        LOG(log_error, logtype_afpd,"Couldn't connect to system bus: %s", error->message);
+        return NULL;
+    }
+
+    if (!(bus_proxy = dbus_g_proxy_new_for_name(bus, "org.freedesktop.DBus",
+                                                "/org/freedesktop/DBus",
+                                                "org.freedesktop.DBus"))) {
+        LOG(log_error, logtype_afpd,"Couldn't create bus proxy");
+        return NULL;
+    }
+
+    if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error,
+                           G_TYPE_STRING, "org.netatalk.AFPStats",
+                           G_TYPE_UINT, 0,
+                           G_TYPE_INVALID,
+                           G_TYPE_UINT, &request_name_result,
+                           G_TYPE_INVALID)) {
+        LOG(log_error, logtype_afpd, "Failed to acquire DBUS name: %s", error->message);
+        return NULL;
+    }
+
+    AFPStatsObj *obj = g_object_new(AFPSTATS_TYPE_OBJECT, NULL);
+    dbus_g_connection_register_g_object(bus, "/org/netatalk/AFPStats", G_OBJECT(obj));
+
+    g_main_loop_run(thread_loop);
+    return thread_loop;
+}
+
+static void my_glib_log(const gchar *log_domain,
+                        GLogLevelFlags log_level,
+                        const gchar *message,
+                        gpointer user_data)
+{
+    LOG(log_error, logtype_afpd, "%s: %s", log_domain, message);
+}
+
+server_child_t *afpstats_get_and_lock_childs(void)
+{
+    pthread_mutex_lock(&childs->servch_lock);
+    return childs;
+}
+
+void afpstats_unlock_childs(void)
+{
+    pthread_mutex_unlock(&childs->servch_lock);
+}
+
+int afpstats_init(server_child_t *childs_in)
+{
+    GThread *thread;
+
+    childs = childs_in;
+    g_type_init();
+    g_thread_init(NULL);
+    dbus_g_thread_init();
+    (void)g_log_set_default_handler(my_glib_log, NULL);
+
+    thread = g_thread_create(afpstats_thread, NULL, TRUE, NULL);
+
+    return 0;
+}
diff --git a/etc/afpd/afpstats.h b/etc/afpd/afpstats.h
new file mode 100644 (file)
index 0000000..9f742ad
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AFPD_AFPSTATS_H
+#define AFPD_AFPSTATS_H
+
+#include <atalk/server_child.h>
+
+extern int afpstats_init(server_child_t *);
+extern server_child_t *afpstats_get_and_lock_childs(void);
+extern void afpstats_unlock_childs(void);
+#endif
diff --git a/etc/afpd/afpstats_obj.c b/etc/afpd/afpstats_obj.c
new file mode 100644 (file)
index 0000000..755d7a5
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <strings.h>
+#include <pwd.h>
+
+#include <glib-object.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <atalk/logger.h>
+#include <atalk/dsi.h>
+
+#include "afpstats.h"
+#include "afpstats_obj.h"
+
+struct AFPStatsObj
+{
+  GObject parent;
+};
+
+struct AFPStatsObjClass
+{
+  GObjectClass parent;
+};
+
+static void afpstats_obj_init(AFPStatsObj *obj)
+{
+}
+
+static void afpstats_obj_class_init(AFPStatsObjClass *klass)
+{
+}
+
+static gpointer afpstats_obj_parent_class = NULL;
+
+static void afpstats_obj_class_intern_init(gpointer klass)
+{
+       afpstats_obj_parent_class = g_type_class_peek_parent(klass);
+       afpstats_obj_class_init((AFPStatsObjClass *)klass);
+}
+
+GType afpstats_obj_get_type(void)
+{
+       static volatile gsize g_define_type_id__volatile = 0;
+       if (g_once_init_enter(&g_define_type_id__volatile)) {
+               GType g_define_type_id = g_type_register_static_simple(
+            G_TYPE_OBJECT,
+            g_intern_static_string("AFPStatsObj"),
+            sizeof(AFPStatsObjClass),
+            (GClassInitFunc)afpstats_obj_class_intern_init,
+                       sizeof(AFPStatsObj),
+                       (GInstanceInitFunc)afpstats_obj_init,
+                       (GTypeFlags)0);
+               g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+       }
+       return g_define_type_id__volatile;
+}
+
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error)
+{
+    gchar **names;
+    server_child_t *childs = afpstats_get_and_lock_childs();
+    afp_child_t *child;
+    struct passwd *pw;
+    int i = 0, j;
+    char buf[256];
+
+    names = g_new(char *, childs->servch_count + 1);
+
+    for (j = 0; j < CHILD_HASHSIZE && i < childs->servch_count; j++) {
+        child = childs->servch_table[j];
+        while (child) {
+            if (child->afpch_valid && (pw = getpwuid(child->afpch_uid))) {
+                time_t time = child->afpch_logintime;
+                strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime(&time));
+                names[i++] = g_strdup_printf("name: %s, pid: %d, logintime: %s, state: %s, volumes: %s",
+                                             pw->pw_name, child->afpch_pid, buf,
+                                             child->afpch_state == DSI_RUNNING ? "active" :
+                                             child->afpch_state == DSI_SLEEPING ? "sleeping" :
+                                             child->afpch_state == DSI_EXTSLEEP ? "sleeping" :
+                                             child->afpch_state == DSI_DISCONNECTED ? "disconnected" :
+                                             "unknown",
+                                             child->afpch_volumes ? child->afpch_volumes : "-"); 
+            }
+            child = child->afpch_next;
+        }
+    }
+    names[i] = NULL;
+    *ret = names;
+
+    afpstats_unlock_childs();
+
+    return TRUE;
+}
diff --git a/etc/afpd/afpstats_obj.h b/etc/afpd/afpstats_obj.h
new file mode 100644 (file)
index 0000000..9d83000
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef AFPSTATS_OBJ_H
+#define AFPSTATS_OBJ_H
+
+#include <glib.h>
+
+typedef struct AFPStatsObj AFPStatsObj;
+typedef struct AFPStatsObjClass AFPStatsObjClass;
+
+GType    afpstats_obj_get_type(void);
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error);
+
+#define AFPSTATS_TYPE_OBJECT              (afpstats_obj_get_type ())
+#define AFPSTATS_OBJECT(object)           (G_TYPE_CHECK_INSTANCE_CAST((object), AFPSTATS_TYPE_OBJECT, AFPStatsObj))
+#define AFPSTATS_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+#define AFPSTATS_IS_OBJECT(object)        (G_TYPE_CHECK_INSTANCE_TYPE((object), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_IS_OBJECT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_OBJECT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+
+#endif /* AFPSTATS_OBJ_H */
diff --git a/etc/afpd/afpstats_service_glue.h b/etc/afpd/afpstats_service_glue.h
new file mode 100644 (file)
index 0000000..65081c4
--- /dev/null
@@ -0,0 +1,121 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+#define __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+
+#include       <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER */
+extern void dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                                     GValue       *return_value,
+                                                                     guint         n_param_values,
+                                                                     const GValue *param_values,
+                                                                     gpointer      invocation_hint,
+                                                                     gpointer      marshal_data);
+void
+dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                         GValue       *return_value G_GNUC_UNUSED,
+                                                         guint         n_param_values,
+                                                         const GValue *param_values,
+                                                         gpointer      invocation_hint G_GNUC_UNUSED,
+                                                         gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
+                                                             gpointer     arg_1,
+                                                             gpointer     arg_2,
+                                                             gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_afpstats_obj_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_afpstats_obj_methods[] = {
+  { (GCallback) afpstats_obj_get_users, dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER, 0 },
+};
+
+const DBusGObjectInfo dbus_glib_afpstats_obj_object_info = {  1,
+  dbus_glib_afpstats_obj_methods,
+  1,
+"org.netatalk.AFPStats\0GetUsers\0S\0ret\0O\0F\0N\0as\0\0\0",
+"\0",
+"\0"
+};
+
index f11d77eb22995a400ff43247cf7ddd4441babdbb..efe2ab6392f2fe1693b2d37ee0c9255fde06c099 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: afs.c,v 1.18 2009-10-15 10:43:13 didg Exp $
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
index 3cba16e0c2ae7eab2fe07c28a4cbc6b11498ad85..f53803e0dfc5f92ebcb5188ede9a146d2922a7b9 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: appl.c,v 1.18.4.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -171,7 +170,11 @@ makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, c
         }
 
         /* next part */
-        if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) {
+        AFP_CNID_START("cnid_resolve");
+        uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen);
+        AFP_CNID_DONE();
+
+        if (uname == NULL) {
             afp_errno = AFPERR_NOOBJ;
             ret = NULL;
             goto exit;
index ce1556b6930ee26b9569ad746dc726c3a722a821..80cd5339f51494b5dd13d0fd240123c53ddf6b49 100644 (file)
@@ -39,6 +39,7 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include <atalk/server_ipc.h>
 #include <atalk/uuid.h>
 #include <atalk/globals.h>
+#include <atalk/unix.h>
 
 #include "auth.h"
 #include "uam_auth.h"
@@ -210,23 +211,6 @@ static int set_auth_switch(const AFPObj *obj, int expired)
     return AFP_OK;
 }
 
-#define GROUPSTR_BUFSIZE 1024
-static const char *print_groups(int ngroups, gid_t *groups)
-{
-    static char groupsstr[GROUPSTR_BUFSIZE];
-    int i;
-    char *s = groupsstr;
-
-    if (ngroups == 0)
-        return "-";
-
-    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
-        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
-    }
-
-    return groupsstr;
-}
-
 static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expired)
 {
 #ifdef ADMIN_GRP
@@ -241,32 +225,8 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi
     LOG(log_note, logtype_afpd, "%s Login by %s",
         afp_versions[afp_version_index].av_name, pwd->pw_name);
 
-    if (initgroups( pwd->pw_name, pwd->pw_gid ) < 0) {
-#ifdef RUN_AS_USER
-        LOG(log_info, logtype_afpd, "running with uid %d", geteuid());
-#else /* RUN_AS_USER */
-        LOG(log_error, logtype_afpd, "login: %s", strerror(errno));
-        return AFPERR_BADUAM;
-#endif /* RUN_AS_USER */
-
-    }
-
-    /* Basically if the user is in the admin group, we stay root */
-
-    if ((obj->ngroups = getgroups( 0, NULL )) < 0 ) {
-        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno) );
-        return AFPERR_BADUAM;
-    }
-
-    if ( NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
-        LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
+    if (set_groups(obj, pwd) != 0)
         return AFPERR_BADUAM;
-    }
-
-    if (( obj->ngroups = getgroups(obj->ngroups, obj->groups )) < 0 ) {
-        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno) );
-        return AFPERR_BADUAM;
-    }
 
 #ifdef ADMIN_GRP
     LOG(log_debug, logtype_afpd, "obj->options.admingid == %d", obj->options.admingid);
@@ -375,6 +335,7 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
         if (dsi->flags & DSI_EXTSLEEP) {
             LOG(log_note, logtype_afpd, "afp_zzz: waking up from extended sleep");
             dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
+            ipc_child_state(obj, DSI_RUNNING);
         }
     } else {
         /* sleep request */
@@ -382,8 +343,10 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
         if (data & AFPZZZ_EXT_SLEEP) {
             LOG(log_note, logtype_afpd, "afp_zzz: entering extended sleep");
             dsi->flags |= DSI_EXTSLEEP;
+            ipc_child_state(obj, DSI_EXTSLEEP);
         } else {
             LOG(log_note, logtype_afpd, "afp_zzz: entering normal sleep");
+            ipc_child_state(obj, DSI_SLEEPING);
         }
     }
 
@@ -912,7 +875,7 @@ int afp_changepw(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rb
         if ( ibuf[0] != '\0' || ibuf[1] != '\0')
             return AFPERR_PARAM;
         ibuf += 2;
-        len = MIN(sizeof(username), strlen(obj->username));
+        len = MIN(sizeof(username) - 1, strlen(obj->username));
         memcpy(username, obj->username, len);
         username[ len ] = '\0';
     }
index 71843e2cd13237863791a396e9e389a8156dcf46..63a9607cdc130acd32faba44906678cfdfc3510e 100644 (file)
@@ -29,6 +29,9 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -109,12 +112,8 @@ struct scrit {
  *
  */
 struct dsitem {
-//     struct dir *dir; /* Structure describing this directory */
-//  cnid_t did;      /* CNID of this directory           */
-       int pidx;        /* Parent's dsitem structure index. */
-       int checked;     /* Have we checked this directory ? */
-       int path_len;
-       char *path;      /* absolute UNIX path to this directory */
+    cnid_t ds_did;         /* CNID of this directory           */
+    int    ds_checked;     /* Have we checked this directory ? */
 };
  
 
@@ -131,7 +130,6 @@ static void clearstack(void)
        save_cidx = -1;
        while (dsidx > 0) {
                dsidx--;
-               free(dstack[dsidx].path);
        }
 }
 
@@ -139,7 +137,6 @@ static void clearstack(void)
 static int addstack(char *uname, struct dir *dir, int pidx)
 {
        struct dsitem *ds;
-       size_t         l, u;
     struct dsitem *tmpds = NULL;
 
        /* check if we have some space on stack... */
@@ -156,23 +153,8 @@ static int addstack(char *uname, struct dir *dir, int pidx)
 
        /* Put new element. Allocate and copy lname and path. */
        ds = dstack + dsidx++;
-//     ds->did = dir->d_did;
-       ds->pidx = pidx;
-       ds->checked = 0;
-       if (pidx >= 0) {
-           l = dstack[pidx].path_len;
-           u = strlen(uname) +1;
-           if (!(ds->path = malloc(l + u + 1) ))
-                       return -1;
-               memcpy(ds->path, dstack[pidx].path, l);
-               ds->path[l] = '/';
-               memcpy(&ds->path[l+1], uname, u);
-               ds->path_len = l +u;
-       }
-       else {
-           ds->path = strdup(uname);
-               ds->path_len = strlen(uname);
-       }
+       ds->ds_did = dir->d_did;
+       ds->ds_checked = 0;
        return 0;
 }
 
@@ -187,9 +169,9 @@ static int reducestack(void)
        }
 
        while (dsidx > 0) {
-               if (dstack[dsidx-1].checked) {
+               if (dstack[dsidx-1].ds_checked) {
                        dsidx--;
-                       free(dstack[dsidx].path);
+//                     free(dstack[dsidx].path);
                } else
                        return dsidx - 1;
        } 
@@ -211,7 +193,7 @@ static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adoub
            
        isdir  = S_ISDIR(path->st.st_mode);
 
-       if (!isdir && (of = of_findname(path))) {
+       if (!isdir && (of = of_findname(vol, path))) {
                adp = of->of_ad;
        } else {
                ad_init(&ad, vol);
@@ -521,7 +503,8 @@ static int catsearch(const AFPObj *obj,
     int num_rounds = NUM_ROUNDS;
     int cwd = -1;
     int error;
-        
+    int unlen;
+
        if (*pos != 0 && *pos != cur_pos) {
                result = AFPERR_CATCHNG;
                goto catsearch_end;
@@ -555,20 +538,24 @@ static int catsearch(const AFPObj *obj,
     start_time = time(NULL);
 
        while ((cidx = reducestack()) != -1) {
-        LOG(log_debug, logtype_afpd, "catsearch: dir: \"%s\"", dstack[cidx].path);
+        if ((currentdir = dirlookup(vol, dstack[cidx].ds_did)) == NULL) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+        LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath));
 
-               error = lchdir(dstack[cidx].path);
+               error = movecwd(vol, currentdir);
 
                if (!error && dirpos == NULL)
                        dirpos = opendir(".");
 
                if (dirpos == NULL)
-                       dirpos = opendir(dstack[cidx].path);
+                       dirpos = opendir(cfrombstr(currentdir->d_fullpath));
 
                if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
-                               dstack[cidx].checked = 1;
+                               dstack[cidx].ds_checked = 1;
                                continue;
                        case EMFILE:
                        case ENFILE:
@@ -583,11 +570,6 @@ static int catsearch(const AFPObj *obj,
                        goto catsearch_end;
                }
 
-        if ((currentdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) {
-            result = AFPERR_MISC;
-            goto catsearch_end;
-        }
-        LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath));
                
                while ((entry = readdir(dirpos)) != NULL) {
                        (*pos)++;
@@ -600,7 +582,7 @@ static int catsearch(const AFPObj *obj,
 
                        memset(&path, 0, sizeof(path));
                        path.u_name = entry->d_name;
-                       if (of_stat(&path) != 0) {
+                       if (of_stat(vol, &path) != 0) {
                                switch (errno) {
                                case EACCES:
                                case ELOOP:
@@ -615,12 +597,13 @@ static int catsearch(const AFPObj *obj,
                                        goto catsearch_end;
                                } 
                        }
-                       if (S_ISDIR(path.st.st_mode)) {
+            switch (S_IFMT & path.st.st_mode) {
+            case S_IFDIR:
                                /* here we can short cut 
                                   ie if in the same loop the parent dir wasn't in the cache
                                   ALL dirsearch_byname will fail.
                                */
-                int unlen = strlen(path.u_name);
+                unlen = strlen(path.u_name);
                 path.d_dir = dircache_search_by_name(vol,
                                                      currentdir,
                                                      path.u_name,
@@ -641,8 +624,12 @@ static int catsearch(const AFPObj *obj,
                                        result = AFPERR_MISC;
                                        goto catsearch_end;
                                } 
-            } else {
+                break;
+            case S_IFREG:
                path.d_dir = currentdir;
+                break;
+            default:
+                continue;
             }
 
                        ccr = crit_check(vol, &path);
@@ -674,7 +661,7 @@ static int catsearch(const AFPObj *obj,
                } /* while ((entry=readdir(dirpos)) != NULL) */
                closedir(dirpos);
                dirpos = NULL;
-               dstack[cidx].checked = 1;
+               dstack[cidx].ds_checked = 1;
        } /* while (current_idx = reducestack()) != -1) */
 
        /* We have finished traversing our tree. Return EOF here. */
@@ -756,11 +743,14 @@ static int catsearch_db(const AFPObj *obj,
 
         LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
 
-        if ((num_matches = cnid_find(vol->v_cdb,
-                                     buffer,
-                                     strlen(uname),
-                                     resbuf,
-                                     sizeof(resbuf))) == -1) {
+        AFP_CNID_START("cnid_find");
+        num_matches = cnid_find(vol->v_cdb,
+                                buffer,
+                                strlen(uname),
+                                resbuf,
+                                sizeof(resbuf));
+        AFP_CNID_DONE();
+        if (num_matches == -1) {
             result = AFPERR_MISC;
             goto catsearch_end;
         }
@@ -776,8 +766,12 @@ static int catsearch_db(const AFPObj *obj,
         memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
         did = cnid;
 
-        if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
+        AFP_CNID_START("cnid_resolve");
+        name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1);
+        AFP_CNID_DONE();
+        if (name == NULL)
             goto next;
+
         LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
             cur_pos, name, ntohl(cnid));
         if ((dir = dirlookup(vol, did)) == NULL)
@@ -789,7 +783,7 @@ static int catsearch_db(const AFPObj *obj,
         path.u_name = name;
         path.m_name = utompath(vol, name, cnid, utf8_encoding(vol->v_obj));
 
-        if (of_stat(&path) != 0) {
+        if (of_stat(vol, &path) != 0) {
             switch (errno) {
             case EACCES:
             case ELOOP:
index c437b7e6b4adb92f2a2c03388b401b3ad2eb8082..9ba96bc595fd5562be47886acae0aba4238dccf2 100644 (file)
@@ -31,6 +31,7 @@
 #include <atalk/netatalk_conf.h>
 #include <atalk/unix.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/errchk.h>
 
 #include "volume.h"
@@ -61,7 +62,7 @@ int setdeskmode(const struct vol *vol, const mode_t mode)
     bstring dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    EC_NEG1( chdir(bdata(dtpath)) );
+    EC_NEG1( chdir(cfrombstr(dtpath)) );
 
     if (( desk = opendir( "." )) == NULL ) {
         if ( chdir( wd ) < 0 ) {
@@ -138,7 +139,7 @@ int setdeskowner(const struct vol *vol, uid_t uid, gid_t gid)
     bstring dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    EC_NEG1( chdir(bdata(dtpath)) );
+    EC_NEG1( chdir(cfrombstr(dtpath)) );
     
     if (( desk = opendir( "." )) == NULL ) {
         if ( chdir( wd ) < 0 ) {
@@ -182,7 +183,7 @@ int setdeskowner(const struct vol *vol, uid_t uid, gid_t gid)
         LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
         EC_FAIL;
     }
-    if (chown(bdata(dtpath), uid, gid ) < 0 && errno != EPERM ) {
+    if (chown(cfrombstr(dtpath), uid, gid ) < 0 && errno != EPERM ) {
         LOG(log_error, logtype_afpd, "setdeskowner: chown %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
     }
 
@@ -203,11 +204,11 @@ static void create_appledesktop_folder(const struct vol * vol)
     dtpath = bfromcstr(vol->v_dbpath);
     bcatcstr(dtpath, "/" APPLEDESKTOP);
 
-    if (lstat(bdata(dtpath), &st) != 0) {
+    if (lstat(cfrombstr(dtpath), &st) != 0) {
 
         become_root();
 
-        if (lstat(bdata(olddtpath), &st) == 0) {
+        if (lstat(cfrombstr(olddtpath), &st) == 0) {
             cmd_argv[0] = "mv";
             cmd_argv[1] = bdata(olddtpath);
             cmd_argv[2] = bdata(dtpath);
@@ -215,10 +216,10 @@ static void create_appledesktop_folder(const struct vol * vol)
             if (run_cmd("mv", cmd_argv) != 0) {
                 LOG(log_error, logtype_afpd, "moving .AppleDesktop from \"%s\" to \"%s\" failed",
                     bdata(olddtpath), bdata(dtpath));
-                mkdir(bdata(dtpath), 0777);
+                mkdir(cfrombstr(dtpath), 0777);
             }
         } else {
-            mkdir(bdata(dtpath), 0777);
+            mkdir(cfrombstr(dtpath), 0777);
         }
 
         unbecome_root();
@@ -830,7 +831,7 @@ static int ad_addcomment(const AFPObj *obj, struct vol *vol, struct path *path,
     }
     
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
@@ -905,7 +906,7 @@ static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t
 
     upath = path->u_name;
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
@@ -982,7 +983,7 @@ static int ad_rmvcomment(const AFPObj *obj, struct vol *vol, struct path *path)
     }
 
     isadir = path_isadir(path);
-    if (isadir || !(of = of_findname(path))) {
+    if (isadir || !(of = of_findname(vol, path))) {
         ad_init(&ad, vol);
         adp = &ad;
     } else
index 7cd08f6f4ec5182a356447306d5ddfa8baa4f173..c145616b314887ea15bcdc7b0327ae9d3b7e1cb7 100644 (file)
@@ -324,7 +324,7 @@ struct dir *dircache_search_by_did(const struct vol *vol, cnid_t cnid)
             return NULL;        /* (1b) */
 
         }
-        if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+        if (ostat(cfrombstr(cdir->d_fullpath), &st, vol_syml_opt(vol)) != 0) {
             LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {missing:\"%s\"}",
                 ntohl(cnid), cfrombstr(cdir->d_fullpath));
             (void)dir_remove(vol, cdir);
@@ -394,7 +394,7 @@ struct dir *dircache_search_by_name(const struct vol *vol,
     }
 
     if (cdir) {
-        if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+        if (ostat(cfrombstr(cdir->d_fullpath), &st, vol_syml_opt(vol)) != 0) {
             LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {missing:\"%s\"}",
                 ntohl(dir->d_did), name, cfrombstr(cdir->d_fullpath));
             (void)dir_remove(vol, cdir);
@@ -434,7 +434,6 @@ struct dir *dircache_search_by_name(const struct vol *vol,
 int dircache_add(const struct vol *vol,
                  struct dir *dir)
 {
-    struct dir *cdir = NULL;
     struct dir key;
     hnode_t *hn;
 
index ed40a26a7faf5e330ea8739386e33979e02de26f..b7dc0701f18eafbf006aba524be3bd9988e64e8c 100644 (file)
@@ -134,7 +134,7 @@ static int netatalk_mkdir(const struct vol *vol, const char *name)
 }
 
 /* ------------------- */
-static int deletedir(int dirfd, char *dir)
+static int deletedir(const struct vol *vol, int dirfd, char *dir)
 {
     char path[MAXPATHLEN + 1];
     DIR *dp;
@@ -165,11 +165,11 @@ static int deletedir(int dirfd, char *dir)
             break;
         }
         strcpy(path + len, de->d_name);
-        if (lstatat(dirfd, path, &st)) {
+        if (ostatat(dirfd, path, &st, vol_syml_opt(vol))) {
             continue;
         }
         if (S_ISDIR(st.st_mode)) {
-            err = deletedir(dirfd, path);
+            err = deletedir(vol, dirfd, path);
         } else {
             err = netatalk_unlinkat(dirfd, path);
         }
@@ -185,7 +185,7 @@ static int deletedir(int dirfd, char *dir)
 }
 
 /* do a recursive copy. */
-static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
+static int copydir(struct vol *vol, struct dir *ddir, int dirfd, char *src, char *dst)
 {
     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
     DIR *dp;
@@ -231,7 +231,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
         }
         strcpy(spath + slen, de->d_name);
 
-        if (lstatat(dirfd, spath, &st) == 0) {
+        if (ostatat(dirfd, spath, &st, vol_syml_opt(vol)) == 0) {
             if (strlen(de->d_name) > drem) {
                 err = AFPERR_PARAM;
                 break;
@@ -239,9 +239,9 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
             strcpy(dpath + dlen, de->d_name);
 
             if (S_ISDIR(st.st_mode)) {
-                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+                if (AFP_OK != (err = copydir(vol, ddir, dirfd, spath, dpath)))
                     goto copydir_done;
-            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+            } else if (AFP_OK != (err = copyfile(vol, vol, ddir, dirfd, spath, dpath, NULL, NULL))) {
                 goto copydir_done;
 
             } else {
@@ -253,7 +253,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
     }
 
     /* keep the same time stamp. */
-    if (lstatat(dirfd, src, &st) == 0) {
+    if (ostatat(dirfd, src, &st, vol_syml_opt(vol)) == 0) {
         ut.actime = ut.modtime = st.st_mtime;
         utime(dst, &ut);
     }
@@ -301,7 +301,7 @@ static int set_dir_errors(struct path *path, const char *where, int err)
  *
  * @note If the passed ret->m_name is mangled, we'll demangle it
  */
-static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
+static int cname_mtouname(const struct vol *vol, struct dir *dir, struct path *ret, int toUTF8)
 {
     static char temp[ MAXPATHLEN + 1];
     char *t;
@@ -331,6 +331,12 @@ static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct p
 
         /* check for OS X mangled filename :( */
         t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+
+        if (curdir == NULL) {
+            /* demangle_osx() calls dirlookup() which might have clobbered curdir */
+            movecwd(vol, dir);
+        }
+
         LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
             ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
 
@@ -497,12 +503,15 @@ struct dir *dirlookup_bypath(const struct vol *vol, const char *path)
                                            cfrombstr(l->entry[i]),
                                            blength(l->entry[i]))) == NULL) {
 
-            if ((cnid = cnid_add(vol->v_cdb,             /* 6. */
-                                 &st,
-                                 did,
-                                 cfrombstr(l->entry[i]),
-                                 blength(l->entry[i]),
-                                 0)) == CNID_INVALID)
+            AFP_CNID_START("cnid_add");
+            cnid = cnid_add(vol->v_cdb,             /* 6. */
+                            &st,
+                            did,
+                            cfrombstr(l->entry[i]),
+                            blength(l->entry[i]),
+                            0);
+            AFP_CNID_DONE();
+            if (cnid == CNID_INVALID)
                 EC_FAIL;
 
             if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */
@@ -607,7 +616,11 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     /* Get it from the database */
     cnid = did;
     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): querying CNID database", ntohl(did));
-    if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) {
+
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen);
+    AFP_CNID_DONE();
+    if (upath == NULL) {
         afp_errno = AFPERR_NOOBJ;
         err = 1;
         goto exit;
@@ -643,7 +656,7 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): stating \"%s\"",
         ntohl(did), cfrombstr(fullpath));
 
-    if (lstat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+    if (ostat(cfrombstr(fullpath), &st, vol_syml_opt(vol)) != 0) { /* 5a */
         switch (errno) {
         case ENOENT:
             afp_errno = AFPERR_NOOBJ;
@@ -815,7 +828,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i
     cnid_t      id;
     struct adouble  ad;
     struct adouble *adp = NULL;
-    bstring fullpath;
+    bstring fullpath = NULL;
 
     AFP_ASSERT(vol);
     AFP_ASSERT(dir);
@@ -912,7 +925,7 @@ exit:
 void dir_free_invalid_q(void)
 {
     struct dir *dir;
-    while (dir = (struct dir *)dequeue(invalid_dircache_entries))
+    while ((dir = (struct dir *)dequeue(invalid_dircache_entries)))
         dir_free(dir);
 }
 
@@ -1181,7 +1194,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
              *   and thus call continue which should terminate the while loop because
              *   len = 0. Ok?
              */
-            if (of_stat(&ret) != 0) { /* 9 */
+            if (of_stat(vol, &ret) != 0) { /* 9 */
                 /*
                  * ret.u_name doesn't exist, might be afp_createfile|dir
                  * that means it should have been the last part
@@ -1299,7 +1312,7 @@ int movecwd(const struct vol *vol, struct dir *dir)
     LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")",
         ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
 
-    if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) {
+    if ((ret = ochdir(cfrombstr(dir->d_fullpath), vol_syml_opt(vol))) != 0 ) {
         LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): %s",
             cfrombstr(dir->d_fullpath), strerror(errno));
         if (ret == 1) {
@@ -1501,10 +1514,6 @@ int getdirparams(const AFPObj *obj,
                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
             } else { /* no appledouble */
                 memset( data, 0, 32 );
-                /* set default view -- this also gets done in ad_open() */
-                ashort = htons(FINDERINFO_CLOSEDVIEW);
-                memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
-
                 /* dot files are by default visible */
                 if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
                     ashort = htons(FINDERINFO_INVISIBLE);
@@ -2174,7 +2183,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
         return err;
     }
 
-    if (of_stat(s_path) < 0) {
+    if (of_stat(vol, s_path) < 0) {
         return AFPERR_MISC;
     }
 
@@ -2195,12 +2204,11 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_setname(&ad, s_path->m_name);
     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
 
-    fce_register_new_dir(s_path);
+    fce_register(FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL, fce_dir);
 
     ad_flush(&ad);
     ad_close(&ad, ADFLAGS_HF);
 
-createdir_done:
     memcpy( rbuf, &dir->d_did, sizeof( uint32_t ));
     *rbuflen = sizeof( uint32_t );
     setvoltime(obj, vol );
@@ -2213,7 +2221,7 @@ createdir_done:
  * newparent curdir
  * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
  */
-int renamedir(const struct vol *vol,
+int renamedir(struct vol *vol,
               int dirfd,
               char *src,
               char *dst,
@@ -2239,11 +2247,11 @@ int renamedir(const struct vol *vol,
         case EXDEV:
             /* this needs to copy and delete. bleah. that means we have
              * to deal with entire directory hierarchies. */
-            if ((err = copydir(vol, dirfd, src, dst)) < 0) {
-                deletedir(-1, dst);
+            if ((err = copydir(vol, newparent, dirfd, src, dst)) < 0) {
+                deletedir(vol, -1, dst);
                 return err;
             }
-            if ((err = deletedir(dirfd, src)) < 0)
+            if ((err = deletedir(vol, dirfd, src)) < 0)
                 return err;
             break;
         default :
@@ -2308,7 +2316,7 @@ int deletecurdir(struct vol *vol)
             /* bail if it's not a symlink */
             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
                 LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
-                    curdir->d_fullpath);
+                    bdata(curdir->d_fullpath));
                 closedir(dp);
                 return AFPERR_DIRNEMPT;
             }
@@ -2330,7 +2338,9 @@ int deletecurdir(struct vol *vol)
 
     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
+        AFP_CNID_START("cnid_delete");
         cnid_delete(vol->v_cdb, fdir->d_did);
+        AFP_CNID_DONE();
         dir_remove( vol, fdir );
     } else {
         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
@@ -2622,7 +2632,7 @@ int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, si
         return path_error(path, AFPERR_NOOBJ);
     }
 
-    if ( !path->st_valid && of_stat(path ) < 0 ) {
+    if ( !path->st_valid && of_stat(vol, path) < 0 ) {
         return( AFPERR_NOOBJ );
     }
     if ( path->st_errno ) {
index 7f3e8c56a1cd135c2405abb5fb3af61a700222dd..eb89c606afc41cce1994851265283bed05d27e9a 100644 (file)
@@ -115,7 +115,7 @@ extern int         getdirparams (const AFPObj *obj, const struct vol *, uint16_t
                                  struct dir *, char *, size_t *);
 
 extern int         setdirparams(struct vol *, struct path *, uint16_t, char *);
-extern int         renamedir(const struct vol *, int, char *, char *, struct dir *,
+extern int         renamedir(struct vol *, int, char *, char *, struct dir *,
                              struct dir *, char *);
 extern int         path_error(struct path *, int error);
 extern void        setdiroffcnt(struct dir *dir, struct stat *st,  uint32_t count);
index d469928c357b465a5781d2f5f51c9aad51fb8a2e..340f79aed6738676acd8341f7c1d7a46171324da 100644 (file)
@@ -284,7 +284,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
         sd.sd_last = sd.sd_buf;
         /* if dir was in the cache we don't have the inode */
-        if (( !o_path->st_valid && lstat( ".", &o_path->st ) < 0 ) ||
+        if (( !o_path->st_valid && ostat(".", &o_path->st, vol_syml_opt(vol)) < 0 ) ||
             (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
         {
             LOG(log_error, logtype_afpd, "enumerate: loop error: %s (%d)", strerror(errno), errno);
@@ -346,16 +346,10 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             sd.sd_last += len + 1;
             continue;
         }
-        memset(&s_path, 0, sizeof(s_path));
 
-        /* conversions on the fly */
-        const char *convname;
+        memset(&s_path, 0, sizeof(s_path));
         s_path.u_name = sd.sd_last;
-        if (ad_convert(sd.sd_last, &s_path.st, vol, &convname) == 0 && convname) {
-            s_path.u_name = (char *)convname;
-        }
-
-        if (of_stat( &s_path) < 0 ) {
+        if (of_stat(vol, &s_path) < 0 ) {
             /* so the next time it won't try to stat it again
              * another solution would be to invalidate the cache with 
              * sd.sd_did = 0 but if it's not ENOENT error it will start again
@@ -366,12 +360,21 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             continue;
         }
 
-        /* Fixup CNID db if ad_convert resulted in a rename (then convname != NULL) */
-        if (convname) {
-            s_path.id = cnid_lookup(vol->v_cdb, &s_path.st, curdir->d_did, sd.sd_last, strlen(sd.sd_last));
-            if (s_path.id != CNID_INVALID) {
-                if (cnid_update(vol->v_cdb, s_path.id, &s_path.st, curdir->d_did, convname, strlen(convname)) != 0)
-                    LOG(log_error, logtype_afpd, "enumerate: error updating CNID of \"%s\"", fullpathname(convname));
+        /* conversions on the fly */
+        const char *convname;
+        if (ad_convert(sd.sd_last, &s_path.st, vol, &convname) == 0) {
+            if (convname) {
+                s_path.u_name = (char *)convname;
+                AFP_CNID_START("cnid_lookup");
+                s_path.id = cnid_lookup(vol->v_cdb, &s_path.st, curdir->d_did, sd.sd_last, strlen(sd.sd_last));
+                AFP_CNID_DONE();
+                if (s_path.id != CNID_INVALID) {
+                    AFP_CNID_START("cnid_update");
+                    int cnid_up_ret = cnid_update(vol->v_cdb, s_path.id, &s_path.st, curdir->d_did, (char *)convname, strlen(convname));
+                    AFP_CNID_DONE();
+                    if (cnid_up_ret != 0)
+                        LOG(log_error, logtype_afpd, "enumerate: error updating CNID of \"%s\"", fullpathname(convname));
+                }
             }
         }
 
index 4cbeb6423ddd316cb28dc32cec5488ce98ed0f81..f6b1cf66178a585c5475aa9f781c8e21ea939612 100644 (file)
@@ -166,13 +166,6 @@ int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
             }
         } else {
             FinderInfo = ad_entry(adp, ADEID_FINDERI);
-
-            if ((adflags & ADFLAGS_DIR)) {
-                /* set default view */
-                uint16 = htons(FINDERINFO_CLOSEDVIEW);
-                memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
-            }
-
             /* Check if FinderInfo equals default and empty FinderInfo*/
             if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
                 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
index 64e8482d233ae4a20c5efe6a1b6ca92e1342c470..68e65905f97e46b65a899e7d66f6ae47271a24cf 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: extattrs.h,v 1.3 2009-10-15 10:43:13 didg Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index b40eafb988a7bf6681faf8fc8a60a2ebcafc40a3..4620378922d763010ac006a700670f5fd194fdc1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010 Mark Williams
+ * Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
  *
  * File change event API for netatalk
  *
 #include <stdlib.h>
 #include <errno.h>
 #include <time.h>
-
-
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <stdbool.h>
 
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 // ONLY USED IN THIS FILE
 #include "fce_api_internal.h"
 
-#define FCE_TRUE 1
-#define FCE_FALSE 0
-
 /* We store our connection data here */
 static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS];
 static int udp_sockets = 0;
-static int udp_initialized = FCE_FALSE;
+static bool udp_initialized = false;
 static unsigned long fce_ev_enabled =
     (1 << FCE_FILE_MODIFY) |
     (1 << FCE_FILE_DELETE) |
@@ -70,9 +67,8 @@ static unsigned long fce_ev_enabled =
     (1 << FCE_FILE_CREATE) |
     (1 << FCE_DIR_CREATE);
 
-static uint64_t tm_used;          /* used for passing to event handler */
 #define MAXIOBUF 1024
-static char iobuf[MAXIOBUF];
+static unsigned char iobuf[MAXIOBUF];
 static const char *skip_files[] = 
 {
        ".DS_Store",
@@ -80,6 +76,15 @@ static const char *skip_files[] =
 };
 static struct fce_close_event last_close_event;
 
+static char *fce_event_names[] = {
+    "",
+    "FCE_FILE_MODIFY",
+    "FCE_FILE_DELETE",
+    "FCE_DIR_DELETE",
+    "FCE_FILE_CREATE",
+    "FCE_DIR_CREATE"
+};
+
 /*
  *
  * Initialize network structs for any listeners
@@ -91,7 +96,7 @@ void fce_init_udp()
     int rv;
     struct addrinfo hints, *servinfo, *p;
 
-    if (udp_initialized == FCE_TRUE)
+    if (udp_initialized == true)
         return;
 
     memset(&hints, 0, sizeof hints);
@@ -106,7 +111,7 @@ void fce_init_udp()
             close(udp_entry->sock);
 
         if ((rv = getaddrinfo(udp_entry->addr, udp_entry->port, &hints, &servinfo)) != 0) {
-            LOG(log_error, logtype_afpd, "fce_init_udp: getaddrinfo(%s:%s): %s",
+            LOG(log_error, logtype_fce, "fce_init_udp: getaddrinfo(%s:%s): %s",
                 udp_entry->addr, udp_entry->port, gai_strerror(rv));
             continue;
         }
@@ -114,7 +119,7 @@ void fce_init_udp()
         /* loop through all the results and make a socket */
         for (p = servinfo; p != NULL; p = p->ai_next) {
             if ((udp_entry->sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
-                LOG(log_error, logtype_afpd, "fce_init_udp: socket(%s:%s): %s",
+                LOG(log_error, logtype_fce, "fce_init_udp: socket(%s:%s): %s",
                     udp_entry->addr, udp_entry->port, strerror(errno));
                 continue;
             }
@@ -122,7 +127,7 @@ void fce_init_udp()
         }
 
         if (p == NULL) {
-            LOG(log_error, logtype_afpd, "fce_init_udp: no socket for %s:%s",
+            LOG(log_error, logtype_fce, "fce_init_udp: no socket for %s:%s",
                 udp_entry->addr, udp_entry->port);
         }
         udp_entry->addrinfo = *p;
@@ -131,12 +136,12 @@ void fce_init_udp()
         freeaddrinfo(servinfo);
     }
 
-    udp_initialized = FCE_TRUE;
+    udp_initialized = true;
 }
 
 void fce_cleanup()
 {
-    if (udp_initialized == FCE_FALSE )
+    if (udp_initialized == false )
         return;
 
     for (int i = 0; i < udp_sockets; i++)
@@ -150,22 +155,21 @@ void fce_cleanup()
             udp_entry->sock = -1;
         }
     }
-    udp_initialized = FCE_FALSE;
+    udp_initialized = false;
 }
 
 /*
  * Construct a UDP packet for our listeners and return packet size
  * */
-static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id )
+static ssize_t build_fce_packet( struct fce_packet *packet, const char *path, int event, uint32_t event_id )
 {
     size_t pathlen = 0;
     ssize_t data_len = 0;
-    uint64_t *t;
 
     /* Set content of packet */
     memcpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) );
     packet->version = FCE_PACKET_VERSION;
-    packet->mode = mode;
+    packet->mode = event;
    
     packet->event_id = event_id; 
 
@@ -180,19 +184,7 @@ static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode
     /* This is the payload len. Means: the packet has len bytes more until packet is finished */
     data_len = FCE_PACKET_HEADER_SIZE + pathlen;
 
-    switch (mode) {
-    case FCE_TM_SIZE:
-        t = (uint64_t *)packet->data;
-        *t = hton64(tm_used);
-        memcpy(packet->data + sizeof(tm_used), path, pathlen);
-        
-        packet->datalen = pathlen + sizeof(tm_used);
-        data_len += sizeof(tm_used);
-        break;
-    default:
-        memcpy(packet->data, path, pathlen);
-        break;
-    }
+    memcpy(packet->data, path, pathlen);
 
     /* return the packet len */
     return data_len;
@@ -231,27 +223,26 @@ static void pack_fce_packet(struct fce_packet *packet, unsigned char *buf, int m
  * Send the fce information to all (connected) listeners
  * We dont give return code because all errors are handled internally (I hope..)
  * */
-static void send_fce_event( char *path, int mode )
+static void send_fce_event(const char *path, int event)
 {    
-    static int first_event = FCE_TRUE;
+    static bool first_event = true;
 
     struct fce_packet packet;
-    void *data = &packet;
     static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */
     time_t now = time(NULL);
 
-    LOG(log_debug, logtype_afpd, "send_fce_event: start");
+    LOG(log_debug, logtype_fce, "send_fce_event: start");
 
     /* initialized ? */
-    if (first_event == FCE_TRUE) {
-        first_event = FCE_FALSE;
+    if (first_event == true) {
+        first_event = false;
         fce_init_udp();
         /* Notify listeners the we start from the beginning */
         send_fce_event( "", FCE_CONN_START );
     }
 
     /* build our data packet */
-    ssize_t data_len = build_fce_packet( &packet, path, mode, ++event_id );
+    ssize_t data_len = build_fce_packet( &packet, path, event, ++event_id );
     pack_fce_packet(&packet, iobuf, MAXIOBUF);
 
     for (int i = 0; i < udp_sockets; i++)
@@ -273,7 +264,7 @@ static void send_fce_event( char *path, int mode )
             
             if (udp_entry->sock == -1) {
                 /* failed again, so go to rest again */
-                LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno  );
+                LOG(log_error, logtype_fce, "Cannot recreate socket for fce UDP connection: errno %d", errno  );
 
                 udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;
                 continue;
@@ -293,7 +284,7 @@ static void send_fce_event( char *path, int mode )
                    udp_entry->addrinfo.ai_addrlen);
 
             /* Rebuild our original data packet */
-            data_len = build_fce_packet( &packet, path, mode, event_id );
+            data_len = build_fce_packet(&packet, path, event, event_id);
             pack_fce_packet(&packet, iobuf, MAXIOBUF);
         }
 
@@ -307,7 +298,7 @@ static void send_fce_event( char *path, int mode )
         /* Problems ? */
         if (sent_data != data_len) {
             /* Argh, socket broke, we close and retry later */
-            LOG(log_error, logtype_afpd, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",
+            LOG(log_error, logtype_fce, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",
                 udp_entry->addr, udp_entry->port, sent_data, data_len, strerror(errno));
 
             close( udp_entry->sock );
@@ -323,7 +314,7 @@ static int add_udp_socket(const char *target_ip, const char *target_port )
         target_port = FCE_DEFAULT_PORT_STRING;
 
     if (udp_sockets >= FCE_MAX_UDP_SOCKS) {
-        LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );
+        LOG(log_error, logtype_fce, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );
         return AFPERR_PARAM;
     }
 
@@ -350,7 +341,7 @@ static void save_close_event(const char *path)
         send_fce_event(last_close_event.path, FCE_FILE_MODIFY);
     }
 
-    LOG(log_debug, logtype_afpd, "save_close_event: %s", path);
+    LOG(log_debug, logtype_fce, "save_close_event: %s", path);
 
     last_close_event.time = now;
     strncpy(last_close_event.path, path, MAXPATHLEN);
@@ -361,66 +352,54 @@ static void save_close_event(const char *path)
  * Dispatcher for all incoming file change events
  *
  * */
-static int register_fce(const char *u_name, int is_dir, int mode)
+int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type)
 {
-    static int first_event = FCE_TRUE;
+    static bool first_event = true;
+    const char *bname;
+
+    if (!(fce_ev_enabled & (1 << event)))
+        return AFP_OK;
+
+    AFP_ASSERT(event >= FCE_FIRST_EVENT && event <= FCE_LAST_EVENT);
+    AFP_ASSERT(path);
+
+    LOG(log_debug, logtype_fce, "register_fce(path: %s, type: %s, event: %s",
+        path, type == fce_dir ? "dir" : "file", fce_event_names[event]);
+
+    bname = basename_safe(path);
 
     if (udp_sockets == 0)
         /* No listeners configured */
         return AFP_OK;
 
-    if (u_name == NULL)
-        return AFPERR_PARAM;
 
        /* do some initialization on the fly the first time */
        if (first_event) {
                fce_initialize_history();
-        first_event = FCE_FALSE;
+        first_event = false;
        }
 
        /* handle files which should not cause events (.DS_Store atc. ) */
-       for (int i = 0; skip_files[i] != NULL; i++)
-       {
-               if (!strcmp( u_name, skip_files[i]))
+       for (int i = 0; skip_files[i] != NULL; i++) {
+               if (strcmp(bname, skip_files[i]) == 0)
                        return AFP_OK;
        }
 
-
-       char full_path_buffer[MAXPATHLEN + 1] = {""};
-       const char *cwd = getcwdpath();
-
-    if (mode == FCE_TM_SIZE) {
-        strlcpy(full_path_buffer, u_name, MAXPATHLEN);
-    } else if (!is_dir || mode == FCE_DIR_DELETE) {
-               if (strlen( cwd ) + strlen( u_name) + 1 >= MAXPATHLEN) {
-                       LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name );
-                       return AFPERR_PARAM;
-               }
-               sprintf( full_path_buffer, "%s/%s", cwd, u_name );
-       } else {
-               if (strlen( cwd ) >= MAXPATHLEN) {
-                       LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd);
-                       return AFPERR_PARAM;
-               }
-               strcpy( full_path_buffer, cwd);
-       }
-
        /* Can we ignore this event based on type or history? */
-       if (!(mode & FCE_TM_SIZE) && fce_handle_coalescation( full_path_buffer, is_dir, mode ))
-       {
-               LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer );
+       if (fce_handle_coalescation(event, path, type)) {
+               LOG(log_debug9, logtype_fce, "Coalesced fc event <%d> for <%s>", event, path);
                return AFP_OK;
        }
 
-       LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer );
-
-    if (mode & FCE_FILE_MODIFY) {
-        save_close_event(full_path_buffer);
-        return AFP_OK;
+    switch (event) {
+    case FCE_FILE_MODIFY:
+        save_close_event(path);
+        break;
+    default:
+        send_fce_event(path, event);
+        break;
     }
 
-    send_fce_event( full_path_buffer, mode );
-
     return AFP_OK;
 }
 
@@ -430,7 +409,7 @@ static void check_saved_close_events(int fmodwait)
 
     /* check if configured holdclose time has passed */
     if (last_close_event.time && ((last_close_event.time + fmodwait) < now)) {
-        LOG(log_debug, logtype_afpd, "check_saved_close_events: sending event: %s", last_close_event.path);
+        LOG(log_debug, logtype_fce, "check_saved_close_events: sending event: %s", last_close_event.path);
         /* yes, send event */
         send_fce_event(&last_close_event.path[0], FCE_FILE_MODIFY);
         last_close_event.path[0] = 0;
@@ -443,106 +422,13 @@ static void check_saved_close_events(int fmodwait)
 /*
  * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c)
  * */
-#ifndef FCE_TEST_MAIN
-
 void fce_pending_events(AFPObj *obj)
 {
-    vol_fce_tm_event();
+    if (!udp_sockets)
+        return;
     check_saved_close_events(obj->options.fce_fmodwait);
 }
 
-int fce_register_delete_file( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_DELETE)))
-        return ret;
-       
-    ret = register_fce( path->u_name, false, FCE_FILE_DELETE );
-
-    return ret;
-}
-int fce_register_delete_dir( char *name )
-{
-    int ret = AFP_OK;
-
-    if (name == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_DIR_DELETE)))
-        return ret;
-       
-    ret = register_fce( name, true, FCE_DIR_DELETE);
-
-    return ret;
-}
-
-int fce_register_new_dir( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_DIR_CREATE)))
-        return ret;
-
-    ret = register_fce( path->u_name, true, FCE_DIR_CREATE );
-
-    return ret;
-}
-
-
-int fce_register_new_file( struct path *path )
-{
-    int ret = AFP_OK;
-
-    if (path == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_CREATE)))
-        return ret;
-
-    ret = register_fce( path->u_name, false, FCE_FILE_CREATE );
-
-    return ret;
-}
-
-int fce_register_file_modification( struct ofork *ofork )
-{
-    int ret = AFP_OK;
-
-    if (ofork == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_FILE_MODIFY)))
-        return ret;
-
-    ret = register_fce(of_name(ofork), false, FCE_FILE_MODIFY );
-    
-    return ret;    
-}
-
-int fce_register_tm_size(const char *vol, size_t used)
-{
-    int ret = AFP_OK;
-
-    if (vol == NULL)
-        return AFPERR_PARAM;
-
-    if (!(fce_ev_enabled & (1 << FCE_TM_SIZE)))
-        return ret;
-
-    tm_used = used;             /* oh what a hack */
-    ret = register_fce(vol, false, FCE_TM_SIZE);
-
-    return ret;
-}
-#endif
-
 /*
  *
  * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times)
@@ -586,8 +472,6 @@ int fce_set_events(const char *events)
             fce_ev_enabled |= (1 << FCE_FILE_CREATE);
         } else if (strcmp(p, "dcre") == 0) {
             fce_ev_enabled |= (1 << FCE_DIR_CREATE);
-        } else if (strcmp(p, "tmsz") == 0) {
-            fce_ev_enabled |= (1 << FCE_TM_SIZE);
         }
     }
 
@@ -605,7 +489,7 @@ void shortsleep( unsigned int us )
 }
 int main( int argc, char*argv[] )
 {
-    int c,ret;
+    int c;
 
     char *port = FCE_DEFAULT_PORT_STRING;
     char *host = "localhost";
@@ -668,7 +552,7 @@ int main( int argc, char*argv[] )
         if (end_time && now >= end_time)
             break;
 
-        register_fce( path, 0, event_code );
+        fce_register(event_code, path, NULL, 0);
         ev_cnt++;
 
         
index 9ed185e53457868b7107b42ee4322caebdcea198..fb5e58e38971602b6de5317e41922e3db7d10461 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <stdbool.h>
 
+#include <atalk/fce_api.h>
+
 #define FCE_MAX_UDP_SOCKS 5     /* Allow a maximum of udp listeners for file change events */
 #define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */
 #define FCE_PACKET_VERSION  1
@@ -20,8 +22,7 @@
 #define FCE_COALESCE_DELETE (1 << 1)
 #define FCE_COALESCE_ALL    (FCE_COALESCE_CREATE | FCE_COALESCE_DELETE)
 
-struct udp_entry
-{
+struct udp_entry {
     int sock;
     char *addr;
     char *port;
@@ -30,12 +31,11 @@ struct udp_entry
     time_t next_try_on_error;      /* In case of error set next timestamp to retry */
 };
 
-struct fce_history
-{
-    unsigned char mode;
-       int is_dir;
-       char path[MAXPATHLEN + 1];
-       struct timeval tv;
+struct fce_history {
+    fce_ev_t       fce_h_event;
+       fce_obj_t      fce_h_type;
+       char           fce_h_path[MAXPATHLEN + 1];
+       struct timeval fce_h_tv;
 };
 
 struct fce_close_event {
@@ -45,7 +45,7 @@ struct fce_close_event {
 
 #define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN)
 
-bool fce_handle_coalescation( char *path, int is_dir, int mode );
+bool fce_handle_coalescation(int event, const char *path, fce_obj_t type);
 void fce_initialize_history();
 
 
index 07b59d73621555cf917eebbc4028ac347860cc37..69f6ea0dabfb9987c767eb6b8ff521bcdc3c1c9e 100644 (file)
@@ -54,9 +54,6 @@
 // ONLY USED IN THIS FILE
 #include "fce_api_internal.h"
 
-#define FCE_TRUE 1
-#define FCE_FALSE 0
-
 /* We store our connection data here */
 static uint32_t coalesce = 0;
 static struct fce_history fce_history_list[FCE_HISTORY_LEN];
@@ -92,7 +89,7 @@ void fce_initialize_history()
        }
 }
 
-bool fce_handle_coalescation( char *path, int is_dir, int mode )
+bool fce_handle_coalescation(int event, const char *path, fce_obj_t type)
 {
        /* These two are used to eval our next index in history */
        /* the history is unsorted, speed should not be a problem, length is 10 */
@@ -104,7 +101,7 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                return false;
 
        /* After a file creation *ALWAYS* a file modification is produced */
-       if ((mode == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
+       if ((event == FCE_FILE_CREATE) && (coalesce & FCE_COALESCE_CREATE))
         return true;
 
        /* get timestamp */
@@ -115,7 +112,7 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                struct fce_history *fh = &fce_history_list[i];
 
                /* Not inited ? */
-               if (fh->tv.tv_sec == 0) {
+               if (fh->fce_h_tv.tv_sec == 0) {
                        /* we can use it for new elements */
                        oldest_entry = 0;
                        oldest_entry_idx = i;
@@ -123,9 +120,9 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
                }
 
                /* Too old ? */
-               if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS) {
+               if (get_ms_difftime(&fh->fce_h_tv, &tv ) > MAX_COALESCE_TIME_MS) {
                        /* Invalidate entry */
-                       fh->tv.tv_sec = 0;
+                       fh->fce_h_tv.tv_sec = 0;
                        oldest_entry = 0;
                        oldest_entry_idx = i;                   
                        continue;
@@ -133,33 +130,33 @@ bool fce_handle_coalescation( char *path, int is_dir, int mode )
 
 
                /* If we find a parent dir wich was created we are done */
-               if ((coalesce & FCE_COALESCE_CREATE) && (fh->mode == FCE_DIR_CREATE)) {
+               if ((coalesce & FCE_COALESCE_CREATE) && (fh->fce_h_event == FCE_DIR_CREATE)) {
                        /* Parent dir ? */
-                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
                                return true;
                }
 
                /* If we find a parent dir we should be DELETED we are done */
                if ((coalesce & FCE_COALESCE_DELETE)
-            && fh->is_dir
-            && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) {
+            && fh->fce_h_type
+            && (event == FCE_FILE_DELETE || event == FCE_DIR_DELETE)) {
                        /* Parent dir ? */
-                       if (!strncmp(fh->path, path, strlen(fh->path)))
+                       if (!strncmp(fh->fce_h_path, path, strlen(fh->fce_h_path)))
                                return true;
                }
 
                /* Detect oldest entry for next new entry */
-               if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) {
-                       oldest_entry = fh->tv.tv_sec;
+               if (oldest_entry_idx == -1 || fh->fce_h_tv.tv_sec < oldest_entry) {
+                       oldest_entry = fh->fce_h_tv.tv_sec;
                        oldest_entry_idx = i;
                }
        }
 
        /* We have a new entry for the history, register it */
-       fce_history_list[oldest_entry_idx].tv = tv;
-       fce_history_list[oldest_entry_idx].mode = mode;
-       fce_history_list[oldest_entry_idx].is_dir = is_dir;
-       strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);
+       fce_history_list[oldest_entry_idx].fce_h_tv = tv;
+       fce_history_list[oldest_entry_idx].fce_h_event = event;
+       fce_history_list[oldest_entry_idx].fce_h_type = type;
+       strncpy(fce_history_list[oldest_entry_idx].fce_h_path, path, MAXPATHLEN);
 
        /* we have to handle this event */
        return false;
index 8cd5f456947333a38372e90487aa78c7819eae70..be1fafe5517411e15ffeb5b31b110bd39fde18a2 100644 (file)
@@ -77,6 +77,7 @@ static int default_type(void *finder)
 /* FIXME path : unix or mac name ? (for now it's unix name ) */
 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
 {
+    struct extmap       *em;
     void                *ad_finder = NULL;
     int                 chk_ext = 0;
 
@@ -85,9 +86,13 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
 
     if (ad_finder) {
         memcpy(data, ad_finder, ADEDLEN_FINDERI);
+        /* default type ? */
+        if (default_type(ad_finder)) 
+            chk_ext = 1;
     }
     else {
         memcpy(data, ufinderi, ADEDLEN_FINDERI);
+        chk_ext = 1;
         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
             uint16_t ashort;
             
@@ -96,7 +101,7 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
         }
     }
 
-    if (islink){
+    if (islink && !vol_syml_opt(vol)) {
         uint16_t linkflag;
         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
         linkflag |= htons(FINDERINFO_ISALIAS);
@@ -105,6 +110,12 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
         memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
     }
 
+    /** Only enter if no appledouble information and no finder information found. */
+    if (chk_ext && (em = getextmap( upath ))) {
+        memcpy(data, em->em_type, sizeof( em->em_type ));
+        memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
+    }
+
     return data;
 }
 
@@ -208,7 +219,10 @@ restart:
            catching moved files */
         adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
 
+        AFP_CNID_START("cnid_add");
            dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
+        AFP_CNID_DONE();
+
            /* Throw errors if cnid_add fails. */
            if (dbcnid == CNID_INVALID) {
             switch (errno) {
@@ -324,6 +338,7 @@ int getmetadata(const AFPObj *obj,
                     || (bconchar(fullpath, '/') != BSTR_OK)
                     || (bcatcstr(fullpath, upath)) != BSTR_OK) {
                     LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+                    bdestroy(fullpath);
                     return AFPERR_MISC;
                 }
 
@@ -444,18 +459,23 @@ int getmetadata(const AFPObj *obj,
             data += sizeof( aint );
             break;
 
-        case FILPBIT_RFLEN :
-            if ( adp ) {
+        case FILPBIT_RFLEN: {
+            off_t rlen;
+            if (adp) {
                 if (adp->ad_rlen > 0xffffffff)
                     aint = 0xffffffff;
                 else
                     aint = htonl( adp->ad_rlen);
             } else {
-                aint = 0;
+                rlen = ad_reso_size(path->u_name, 0, NULL);
+                if (rlen > 0xffffffff)
+                    rlen = 0xffffffff;
+                aint = htonl(rlen);
             }
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
+        }
 
             /* Current client needs ProDOS info block for this file.
                Use simple heuristic and let the Mac "type" string tell
@@ -523,15 +543,18 @@ int getmetadata(const AFPObj *obj,
             data += sizeof( aint );
             break;
         case FILPBIT_EXTRFLEN:
-            aint = 0;
-            if (adp) 
+            if (adp) {
                 aint = htonl(adp->ad_rlen >> 32);
-            memcpy(data, &aint, sizeof( aint ));
-            data += sizeof( aint );
-            if (adp) 
+                memcpy(data, &aint, sizeof( aint ));
+                data += sizeof( aint );
                 aint = htonl(adp->ad_rlen);
-            memcpy(data, &aint, sizeof( aint ));
-            data += sizeof( aint );
+                memcpy(data, &aint, sizeof( aint ));
+                data += sizeof( aint );
+            } else {
+                int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
+                memcpy(data, &rlen, sizeof(rlen));
+                data += sizeof(rlen);
+            }
             break;
         case FILPBIT_UNIXPR :
             /* accessmode may change st_mode with ACLs */
@@ -594,7 +617,7 @@ int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct pat
     struct adouble     ad, *adp;
     int                 opened = 0;
     int rc;    
-    int flags;
+    int flags; /* uninitialized ok */
 
     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
 
@@ -630,7 +653,9 @@ int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct pat
         }
     }
     rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
-    ad_close(adp, ADFLAGS_HF | flags);
+
+    if (opened)
+        ad_close(adp, ADFLAGS_HF | flags);
 
     return( rc );
 }
@@ -675,7 +700,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     ad_init(&ad, vol);
     
     /* if upath is deleted we already in trouble anyway */
-    if ((of = of_findname(s_path))) {
+    if ((of = of_findname(vol, s_path))) {
         if (creatf)
             return AFPERR_BUSY;
         else
@@ -737,9 +762,8 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 createfile_iderr:
     ad_flush(&ad);
     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
-    fce_register_new_file(s_path);
+    fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
 
-createfile_done:
     curdir->d_offcnt++;
 
     setvoltime(obj, vol );
@@ -828,7 +852,10 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
     uint16_t           bitmap = f_bitmap;
     uint32_t           cdate,bdate;
     u_char              finder_buf[32];
-    int symlinked = 0;
+    int symlinked = S_ISLNK(path->st.st_mode);
+    int fp;
+    ssize_t len;
+    char symbuf[MAXPATHLEN+1];
 
 #ifdef DEBUG
     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
@@ -870,29 +897,33 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
             break;
         case FILPBIT_FINFO :
             change_mdate = 1;
-            memcpy(finder_buf, buf, 32 );
-            if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
-                int fp;
-                ssize_t len;
-                int erc=1;
-                char buf[PATH_MAX+1];
-                if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
-                    if ((len = read(fp, buf, PATH_MAX+1))) {
-                        if (unlink(path->u_name) == 0) {
-                            buf[len] = 0;
-                            erc = symlink(buf, path->u_name);
-                            if (!erc)
-                                of_stat(path);
-                        }
-                    }
-                    close(fp);
+            if (memcmp(buf,"slnkrhap",8) == 0
+                && !(S_ISLNK(path->st.st_mode))
+                && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
+                /* request to turn this into a symlink */
+                if ((fp = open(path->u_name, O_RDONLY)) == -1) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
                 }
-                if (erc != 0) {
-                    err=AFPERR_BITMAP;
+                len = read(fp, symbuf, MAXPATHLEN);
+                close(fp);
+                if (!(len > 0)) {
+                    err = AFPERR_MISC;
                     goto setfilparam_done;
                 }
+                if (unlink(path->u_name) != 0) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                symbuf[len] = 0;
+                if (symlink(symbuf, path->u_name) != 0) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                of_stat(vol, path);
                 symlinked = 1;
             }
+            memcpy(finder_buf, buf, 32 );
             buf += 32;
             break;
         case FILPBIT_UNIXPR :
@@ -970,6 +1001,12 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
         isad = 0;
     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
         ad_setname(adp, path->m_name);
+        cnid_t id;
+        if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
+            LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
+            return AFPERR_MISC;
+        }
+        (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
     }
     
     bit = 0;
@@ -1002,6 +1039,17 @@ int setfilparams(const AFPObj *obj, struct vol *vol,
             ad_setdate(adp, AD_DATE_BACKUP, bdate);
             break;
         case FILPBIT_FINFO :
+            if (default_type( ad_entry( adp, ADEID_FINDERI ))
+                    && ( 
+                     ((em = getextmap( path->m_name )) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+                     || ((em = getdefextmap()) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+            )) {
+                memcpy(finder_buf, ufinderi, 8 );
+            }
             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
             break;
         case FILPBIT_UNIXPR :
@@ -1064,7 +1112,7 @@ setfilparam_done:
  * adp         adouble struct of src file, if open, or & zeroed one
  *
  */
-int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
+int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
 {
     int                rc;
 
@@ -1089,7 +1137,7 @@ int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *n
                /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
                return AFPERR_OLOCK; /* little lie */
            }
-            if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
+            if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
                 /* on error copyfile delete dest */
                 return( rc );
             }
@@ -1323,7 +1371,7 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
         goto copy_exit;
     }
 
-    if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
+    if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
         retvalue = err;
         goto copy_exit;
     }
@@ -1341,8 +1389,9 @@ copy_exit:
  * because we are doing it elsewhere.
  * currently if newname is NULL then adp is NULL. 
  */
-int copyfile(const struct vol *s_vol,
-             const struct vol *d_vol, 
+int copyfile(struct vol *s_vol,
+             struct vol *d_vol, 
+             struct dir *d_dir, 
              int sfd,
              char *src,
              char *dst,
@@ -1364,13 +1413,17 @@ int copyfile(const struct vol *s_vol,
         adp = &ads;
     }
 
-    adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
+    adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
 
     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
         ret_err = errno;
         goto done;
     }
 
+    if (!AD_META_OPEN(adp))
+        /* no resource fork, don't create one for dst file */
+        adflags &= ~ADFLAGS_HF;
+
     if (!AD_RSRC_OPEN(adp))
         /* no resource fork, don't create one for dst file */
         adflags &= ~ADFLAGS_RF;
@@ -1403,12 +1456,25 @@ int copyfile(const struct vol *s_vol,
     if (err < 0)
        ret_err = errno;
 
-    if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
-        /* set the new name in the resource fork */
-        ad_copy_header(&add, adp);
-        ad_setname(&add, newname);
-        ad_flush( &add );
+    if (AD_META_OPEN(&add)) {
+        if (AD_META_OPEN(adp))
+            ad_copy_header(&add, adp);
+        ad_setname(&add, dst);
+        cnid_t id;
+        struct stat stdest;
+        if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
+            ret_err = errno;
+            goto error;
+        }
+        if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
+            ret_err = EINVAL;
+            goto error;
+        }
+        (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
+        ad_flush(&add);
     }
+
+error:
     ad_close( adp, adflags );
 
     if (ad_close( &add, adflags ) <0) {
@@ -1525,7 +1591,7 @@ int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
         adp = &ad;
     }
 
-    if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
+    if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
         adflags |= ADFLAGS_RF;
         /* FIXME we have a pb here because we want to know if a file is open 
          * there's a 'priority inversion' if you can't open the ressource fork RW
@@ -1547,8 +1613,15 @@ int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
         err = AFPERR_BUSY;
     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
         cnid_t id;
-        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
-            cnid_delete(vol->v_cdb, id);
+        if (checkAttrib) {
+            AFP_CNID_START("cnid_get");
+            id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
+            AFP_CNID_DONE();
+            if (id) {
+                AFP_CNID_START("cnid_delete");
+                cnid_delete(vol->v_cdb, id);
+                AFP_CNID_DONE();
+            }
         }
     }
 
@@ -1621,7 +1694,10 @@ int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
             return AFPERR_PARAM;
     }
     st = &s_path->st;
-    if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
+    AFP_CNID_START("cnid_lookup");
+    id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
+    AFP_CNID_DONE();
+    if (id) {
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFPERR_EXISTID;
@@ -1650,11 +1726,13 @@ static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
     cnid_t        did  = param->did;
     cnid_t       aint;
     
-    if ( lstat(de->d_name, &path.st) < 0 )
+    if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
         return 0;
     
     /* update or add to cnid */
+    AFP_CNID_START("cnid_add");
     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
+    AFP_CNID_DONE();
 
     return 0;
 }
@@ -1675,7 +1753,7 @@ reenumerate_id(struct vol *vol, char *name, struct dir *dir)
     }
     
     /* FIXME use of_statdir ? */
-    if (lstat(name, &st)) {
+    if (ostat(name, &st, vol_syml_opt(vol))) {
        return -1;
     }
 
@@ -1733,7 +1811,10 @@ int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
         return AFPERR_NOID;
     }
 retry:
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
+    AFP_CNID_DONE();
+    if (upath == NULL) {
         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
     }
 
@@ -1754,7 +1835,7 @@ retry:
 
     memset(&path, 0, sizeof(path));
     path.u_name = upath;
-    if ( of_stat(&path) < 0 ) {
+    if (of_stat(vol, &path) < 0 ) {
 #ifdef ESTALE
         /* with nfs and our working directory is deleted */
        if (errno == ESTALE) {
@@ -1836,7 +1917,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     ibuf += sizeof(id);
     fileid = id;
 
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
+    AFP_CNID_START("cnid_resolve");
+    upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
+    AFP_CNID_DONE();
+        if (upath == NULL) {
         return AFPERR_NOID;
     }
 
@@ -1849,7 +1933,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     err = AFP_OK;
-    if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
+    if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1869,7 +1953,9 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
         return AFPERR_BADTYPE;
 
 delete:
+    AFP_CNID_START("cnid_delete");
     if (cnid_delete(vol->v_cdb, fileid)) {
+        AFP_CNID_DONE();
         switch (errno) {
         case EROFS:
             return AFPERR_VLOCK;
@@ -1880,7 +1966,7 @@ delete:
             return AFPERR_PARAM;
         }
     }
-
+    AFP_CNID_DONE();
     return err;
 }
 
@@ -1912,7 +1998,7 @@ static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct p
         return NULL;
     }
     
-    if ((*of = of_findname(path))) {
+    if ((*of = of_findname(vol, path))) {
         /* reuse struct adouble so it won't break locks */
         adp = (*of)->of_ad;
     }
@@ -2008,7 +2094,9 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     
     /* look for the source cnid. if it doesn't exist, don't worry about
      * it. */
+    AFP_CNID_START("cnid_lookup");
     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
+    AFP_CNID_DONE();
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
         err = afp_errno; /* was AFPERR_PARAM */
@@ -2051,17 +2139,21 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
 
     /* look for destination id. */
     upath = path->u_name;
+    AFP_CNID_START("cnid_lookup");
     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
+    AFP_CNID_DONE();
 
     /* construct a temp name.
      * NOTE: the temp file will be in the dest file's directory. it
      * will also be inaccessible from AFP. */
     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
-    if (!mktemp(temp)) {
+    int fd;
+    if ((fd = mkstemp(temp)) == -1) {
         err = AFPERR_MISC;
         goto err_exchangefile;
     }
-    
+    close(fd);
+
     if (crossdev) {
         /* FIXME we need to close fork for copy, both s_of and d_of are null */
        ad_close(adsp, ADFLAGS_HF);
@@ -2069,32 +2161,39 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     /* now, quickly rename the file. we error if we can't. */
-    if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
         goto err_exchangefile;
     of_rename(vol, s_of, sdir, spath, curdir, temp);
 
     /* rename destination to source */
-    if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
         goto err_src_to_tmp;
     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
-    if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
         goto err_dest_to_src;
     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
 
-    /* id's need switching. src -> dest and dest -> src. 
+    /* 
+     * id's need switching. src -> dest and dest -> src. 
      * we need to re-stat() if it was a cross device copy.
-    */
-    if (sid)
+     */
+    if (sid) {
+        AFP_CNID_START("cnid_delete");        
         cnid_delete(vol->v_cdb, sid);
-    if (did)
+        AFP_CNID_DONE();
+    }
+    if (did) {
+        AFP_CNID_START("cnid_delete");
         cnid_delete(vol->v_cdb, did);
+        AFP_CNID_DONE();
+    }
 
-    if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
+    if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
        ||
-       (sid && ( (crossdev && lstat(p, &destst) < 0) ||
+        (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
     ) {
         switch (errno) {
@@ -2154,17 +2253,17 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
      * properly. */
 err_temp_to_dest:
     /* rename dest to temp */
-    renamefile(vol, -1, upath, temp, temp, adsp);
+    renamefile(vol, curdir, -1, upath, temp, temp, adsp);
     of_rename(vol, s_of, curdir, upath, curdir, temp);
 
 err_dest_to_src:
     /* rename source back to dest */
-    renamefile(vol, -1, p, upath, path->m_name, addp);
+    renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
 
 err_src_to_tmp:
     /* rename temp back to source */
-    renamefile(vol, -1, temp, p, spath, adsp);
+    renamefile(vol, curdir, -1, temp, p, spath, adsp);
     of_rename(vol, s_of, curdir, temp, sdir, spath);
 
 err_exchangefile:
index aa9f3b4449a8ea386adf328e8b3f97d39cf759b6..1d0ff45ea16b4f3aeaf234b10d5f0c667179e1b6 100644 (file)
@@ -51,12 +51,6 @@ extern const u_char  ufinderi[];
 #define FILPBIT_EXTRFLEN 14
 #define FILPBIT_UNIXPR   15
 
-struct extmap {
-    char               *em_ext;
-    char               em_creator[ 4 ];
-    char               em_type[ 4 ];
-};
-
 #define kTextEncodingUTF8 0x08000103
 
 typedef enum {
@@ -111,8 +105,8 @@ extern struct extmap        *getdefextmap (void);
 extern int getfilparams (const AFPObj *obj, struct vol *, uint16_t, struct path *,
                          struct dir *, char *buf, size_t *, int);
 extern int setfilparams (const AFPObj *obj, struct vol *, struct path *, uint16_t, char *);
-extern int renamefile   (const struct vol *, int, char *, char *, char *, struct adouble *);
-extern int copyfile     (const struct vol *, const struct vol *, int, char *, char *, char *, struct adouble *);
+extern int renamefile   (struct vol *, struct dir *, int, char *, char *, char *, struct adouble *);
+extern int copyfile     (struct vol *, struct vol *, struct dir *, int, char *, char *, char *, struct adouble *);
 extern int deletefile   (const struct vol *, int, char *, int);
 
 extern int getmetadata  (const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path, 
index 56cbaccffff2d32d59697fb6acad396589284bca..8feac755be3ad09082ec4627fb29e1b40c648207 100644 (file)
@@ -218,7 +218,7 @@ int check_name(const struct vol *vol, char *name)
     move and rename sdir:oldname to curdir:newname in volume vol
     special care is needed for lock   
 */
-static int moveandrename(const struct vol *vol,
+static int moveandrename(struct vol *vol,
                          struct dir *sdir,
                          int sdir_fd,
                          char *oldname,
@@ -248,7 +248,9 @@ static int moveandrename(const struct vol *vol,
     if (!isdir) {
         if ((oldunixname = strdup(mtoupath(vol, oldname, sdir->d_did, utf8_encoding(vol->v_obj)))) == NULL)
             return AFPERR_PARAM; /* can't convert */
+        AFP_CNID_START("cnid_get");
         id = cnid_get(vol->v_cdb, sdir->d_did, oldunixname, strlen(oldunixname));
+        AFP_CNID_DONE();
 
 #ifndef HAVE_ATFUNCS
         /* Need full path */
@@ -263,7 +265,7 @@ static int moveandrename(const struct vol *vol,
 #ifdef HAVE_ATFUNCS
         opened = of_findnameat(sdir_fd, &path);
 #else
-        opened = of_findname(&path);
+        opened = of_findname(vol, &path);
 #endif /* HAVE_ATFUNCS */
 
         if (opened) {
@@ -346,10 +348,10 @@ static int moveandrename(const struct vol *vol,
     if ( !isdir ) {
         path.st_valid = 1;
         path.st_errno = errno;
-        if (of_findname(&path)) {
+        if (of_findname(vol, &path)) {
             rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
         } else {
-            rc = renamefile(vol, sdir_fd, oldunixname, upath, newname, adp );
+            rc = renamefile(vol, curdir, sdir_fd, oldunixname, upath, newname, adp );
             if (rc == AFP_OK)
                 of_rename(vol, opened, sdir, oldname, curdir, newname);
         }
@@ -378,7 +380,9 @@ static int moveandrename(const struct vol *vol,
         }
 
         /* fix up the catalog entry */
+        AFP_CNID_START("cnid_update");
         cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
+        AFP_CNID_DONE();
     }
 
 exit:
@@ -521,11 +525,17 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
                 delcnid = deldir->d_did;
                 dir_remove(vol, deldir);
             }
-            if (delcnid == CNID_INVALID)
+            if (delcnid == CNID_INVALID) {
+                AFP_CNID_START("cnid_get");
                 delcnid = cnid_get(vol->v_cdb, curdir->d_did, upath, strlen(upath));
-            if (delcnid != CNID_INVALID)
+                AFP_CNID_DONE();
+            }
+            if (delcnid != CNID_INVALID) {
+                AFP_CNID_START("cnid_delete");
                 cnid_delete(vol->v_cdb, delcnid);
-            fce_register_delete_dir(upath);
+                AFP_CNID_DONE();
+            }
+            fce_register(FCE_DIR_DELETE, fullpathname(upath), NULL, fce_dir);
         } else {
             /* we have to cache this, the structs are lost in deletcurdir*/
             /* but we need the positive returncode to send our event */
@@ -533,10 +543,10 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             if ((dname = bstrcpy(curdir->d_u_name)) == NULL)
                 return AFPERR_MISC;
             if ((rc = deletecurdir(vol)) == AFP_OK)
-                fce_register_delete_dir(cfrombstr(dname));
+                fce_register(FCE_DIR_DELETE, fullpathname(cfrombstr(dname)), NULL, fce_dir);
             bdestroy(dname);
         }
-    } else if (of_findname(s_path)) {
+    } else if (of_findname(vol, s_path)) {
         rc = AFPERR_BUSY;
     } else {
         /* it's a file st_valid should always be true
@@ -547,7 +557,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             rc = AFPERR_NOOBJ;
         } else {
             if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK) {
-                               fce_register_delete_file( s_path );
+                               fce_register(FCE_FILE_DELETE, fullpathname(upath), NULL, fce_file);
                 if (vol->v_tm_used < s_path->st.st_size)
                     vol->v_tm_used = 0;
                 else 
@@ -582,8 +592,10 @@ char *absupath(const struct vol *vol, struct dir *dir, char *u)
         return NULL;
     if (bcatcstr(path, u) != BSTR_OK)
         return NULL;
-    if (path->slen > MAXPATHLEN)
+    if (path->slen > MAXPATHLEN) {
+        bdestroy(path);
         return NULL;
+    }
 
     LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
 
@@ -703,8 +715,8 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         if (!isdir && !vol_unix_priv(vol)) {
             int  admode = ad_mode("", 0777) | vol->v_fperm;
 
-            setfilmode(upath, admode, NULL, vol->v_umask);
-            vol->vfs->vfs_setfilmode(vol, upath, admode, NULL);
+            setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
+            vol->vfs->vfs_setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
         }
         setvoltime(obj, vol );
     }
index 9c3ed7d2f1be64ce11c14b5814850195ca6c7742..0c988828d609660bc096c8cf01c944f6f8e633dc 100644 (file)
@@ -22,6 +22,7 @@
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
+#include <atalk/bstrlib.h>
 #include <atalk/bstradd.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
@@ -250,7 +251,6 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     struct stat     *st;
     uint16_t        bshort;
     struct path     *s_path;
-    struct stat xxx;
 
     ibuf++;
     fork = *ibuf++;
@@ -316,7 +316,7 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         }
     }
 
-    if ((opened = of_findname(s_path))) {
+    if ((opened = of_findname(vol, s_path))) {
         adsame = opened->of_ad;
     }
 
@@ -328,8 +328,6 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     } else {
         eid = ADEID_RFORK;
         adflags |= ADFLAGS_RF;
-        if (!(access & OPENACC_WR))
-            adflags |= ADFLAGS_NORF;
     }
 
     if (access & OPENACC_WR) {
@@ -388,6 +386,16 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
 
     if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
         ofork->of_flags |= AFPFORK_META;
+        if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) {
+            LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath);
+            cnid_t id;
+            if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
+                LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
+                goto openfork_err;
+            }
+            (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp);
+            ad_flush(ofork->of_ad);
+        }
     } else {
         switch (errno) {
         case EACCES:
@@ -575,7 +583,7 @@ int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, s
             goto afp_setfork_err;
         }
 
-        err = ad_rtruncate(ofork->of_ad, size);
+        err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size);
         if (st_size > size)
             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
         if (err < 0)
@@ -733,8 +741,6 @@ int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t
 static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
 {
     ssize_t cc;
-    int eof = 0;
-    char *p, *q;
 
     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
     if ( cc < 0 ) {
@@ -793,6 +799,8 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         goto afp_read_err;
     }
 
+    AFP_READ_START((long)reqcount);
+
     /* reqcount isn't always truthful. we need to deal with that. */
     size = ad_size(ofork->of_ad, eid);
 
@@ -800,7 +808,7 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")",
         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size);
 
-    if (offset > size) {
+    if (offset >= size) {
         err = AFPERR_EOF;
         goto afp_read_err;
     }
@@ -839,11 +847,13 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     }
 #endif
 
-    *rbuflen = MIN(reqcount, *rbuflen);
+    *rbuflen = MIN(reqcount, dsi->server_quantum);
 
-    err = read_file(ofork, eid, offset, rbuf, rbuflen);
-    if (err < 0)
+    cc = read_file(ofork, eid, offset, ibuf, rbuflen);
+    if (cc < 0) {
+        err = cc;
         goto afp_read_done;
+    }
 
     LOG(log_debug, logtype_afpd,
         "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
@@ -856,18 +866,22 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
      * we know that we're sending some data. if we fail, something
      * horrible happened.
      */
-    if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
+    if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0)
         goto afp_read_exit;
     *rbuflen = cc;
 
     while (*rbuflen > 0) {
-        cc = read_file(ofork, eid, offset, rbuf, rbuflen);
+        /*
+         * This loop isn't really entered anymore, we've already
+         * sent the whole requested block in dsi_readinit().
+         */
+        cc = read_file(ofork, eid, offset, ibuf, rbuflen);
         if (cc < 0)
             goto afp_read_exit;
 
         offset += *rbuflen;
         /* dsi_read() also returns buffer size of next allocation */
-        cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
+        cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */
         if (cc < 0)
             goto afp_read_exit;
         *rbuflen = cc;
@@ -885,6 +899,8 @@ afp_read_exit:
 afp_read_done:
     if (obj->options.flags & OPTION_AFP_READ_LOCK)
         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
+
+    AFP_READ_DONE();
     return err;
 
 afp_read_err:
@@ -1039,7 +1055,7 @@ int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, s
         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc");
 
     if (of_closefork(obj, ofork) < 0 ) {
-        LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
+        LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) );
         return( AFPERR_PARAM );
     }
 
@@ -1051,7 +1067,6 @@ static ssize_t write_file(struct ofork *ofork, int eid,
                           off_t offset, char *rbuf,
                           size_t rbuflen)
 {
-    char *p, *q;
     ssize_t cc;
 
     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
@@ -1090,7 +1105,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     uint16_t        ofrefnum;
     ssize_t         cc;
     DSI             *dsi = obj->dsi;
-    char            *rcvbuf = dsi->commands;
+    char            *rcvbuf = (char *)dsi->commands;
     size_t          rcvbuflen = dsi->server_quantum;
 
     /* figure out parameters */
@@ -1156,6 +1171,8 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    AFP_WRITE_START((long)reqcount);
+
     saveoff = offset;
     if (obj->options.flags & OPTION_AFP_READ_LOCK) {
         if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) {
@@ -1230,6 +1247,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
 
     *rbuflen = set_off_t (offset, rbuf, is64);
+    AFP_WRITE_DONE();
     return( AFP_OK );
 
 afp_write_err:
index d7120e4778b163927bd17b31c2f5ffd4872aa3a9..5b63601f992fe139e2b67bf4d724199a6c873c52 100644 (file)
@@ -59,13 +59,13 @@ extern struct ofork *of_alloc    (struct vol *, struct dir *,
                                                       struct stat *);
 extern void         of_dealloc   (struct ofork *);
 extern struct ofork *of_find     (const uint16_t);
-extern struct ofork *of_findname (struct path *);
+extern struct ofork *of_findname (const struct vol *vol, struct path *);
 extern int          of_rename    (const struct vol *,
                                           struct ofork *,
                                           struct dir *, const char *,
                                           struct dir *, const char *);
 extern int          of_flush     (const struct vol *);
-extern int          of_stat      (struct path *);
+extern int          of_stat      (const struct vol *vol, struct path *);
 extern int          of_statdir   (struct vol *vol, struct path *);
 extern int          of_closefork (const AFPObj *obj, struct ofork *ofork);
 extern void         of_closevol  (const AFPObj *obj, const struct vol *vol);
diff --git a/etc/afpd/gettok.c b/etc/afpd/gettok.c
deleted file mode 100644 (file)
index fd48db6..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * $Id: gettok.c,v 1.6 2009-10-13 22:55:37 didg Exp $
- *
- * Copyright (c) 1990,1994 Regents of The University of Michigan.
- * All Rights Reserved.  See COPYRIGHT.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <sys/param.h>
-#include <string.h>
-#include <ctype.h>
-#include <pwd.h>
-
-#include <atalk/globals.h>
-
-static char    *l_curr;
-static char    *l_end;
-
-void initline( int len, char *line)
-{
-    l_curr = line;
-    l_end = line + len;
-}
-
-#define ST_QUOTE       0
-#define ST_WORD                1
-#define ST_BEGIN       2
-
-int
-parseline(int len, char *token)
-{
-    char       *p, *e;
-    int                state;
-
-    state = ST_BEGIN;
-    p = token;
-    e = token + len;
-
-    for (;;) {
-        if ( l_curr > l_end ) {                        /* end of line */
-            *token = '\0';
-            return( -1 );
-        }
-
-        switch ( *l_curr ) {
-        case '"' :
-            if ( state == ST_QUOTE ) {
-                state = ST_WORD;
-            } else {
-                state = ST_QUOTE;
-            }
-            break;
-
-        case '\0' :
-        case '\t' :
-        case '\n' :
-        case ' ' :
-            if ( state == ST_WORD ) {
-                *p = '\0';
-                return( p - token );
-            }
-            if ( state != ST_QUOTE ) {
-                break;
-            }
-            /* FALL THROUGH */
-
-        default :
-            if ( state == ST_BEGIN ) {
-                state = ST_WORD;
-            }
-            if ( p > e ) {                     /* end of token */
-                *token = '\0';
-                return( -1 );
-            }
-            *p++ = *l_curr;
-            break;
-        }
-
-        l_curr++;
-    }
-}
-
-#ifdef notdef
-void parseline(char *token, char *user)
-{
-    char               *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
-    struct passwd      *pwent;
-    int                        quoted = 0;
-
-    while ( isspace( *p )) {
-        p++;
-    }
-
-    /*
-     * If we've reached the end of the line, or a comment,
-     * don't return any more tokens.
-     */
-    if ( *p == '\0' || *p == '#' ) {
-        *token = '\0';
-        return;
-    }
-
-    if ( *p == '"' ) {
-        p++;
-        quoted = 1;
-    }
-    while ( *p != '\0' && ( quoted || !isspace( *p ))) {
-        if ( *p == '"' ) {
-            if ( quoted ) {
-                *t = '\0';
-                break;
-            }
-            quoted = 1;
-            p++;
-        } else {
-            *t++ = *p++;
-        }
-    }
-    pos = p;
-    *t = '\0';
-
-    /*
-     * We got to the end of the line without closing an open quote
-     */
-    if ( *p == '\0' && quoted ) {
-        *token = '\0';
-        return;
-    }
-
-    t = token;
-    if ( *t == '~' ) {
-        t++;
-        if ( *t == '\0' || *t == '/' ) {
-            u = user;
-            if ( *t == '/' ) {
-                t++;
-            }
-        } else {
-            u = t;
-            if (( q = strchr( t, '/' )) == NULL ) {
-                t = "";
-            } else {
-                *q = '\0';
-                t = q + 1;
-            }
-        }
-        if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
-            *token = '\0';
-            return;
-        }
-        strcpy( buf, pwent->pw_dir );
-        if ( *t != '\0' ) {
-            strcat( buf, "/" );
-            strcat( buf, t );
-        }
-        strcpy( token, buf );
-    }
-    return;
-}
-#endif /* notdef */
index b7471ccb19ec821b7b7a6559272b02ec6736c985..a7c624d48b4c8603ceb9aca2ed046001d89b9da7 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp $
  * $Name:  $
  */
 #define NDEBUG
@@ -26,7 +25,6 @@
 #include "hash.h"
 
 #ifdef KAZLIB_RCSID
-static const char rcsid[] = "$Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp $";
 #endif
 
 #define INIT_BITS   6
@@ -58,7 +56,6 @@ static const char rcsid[] = "$Id: hash.c,v 1.4 2009-11-19 10:37:43 franklahm Exp
 static hnode_t *hnode_alloc(void *context);
 static void hnode_free(hnode_t *node, void *context);
 static hash_val_t hash_fun_default(const void *key);
-static hash_val_t hash_fun2(const void *key);
 static int hash_comp_default(const void *key1, const void *key2);
 
 int hash_val_t_bit;
@@ -850,6 +847,13 @@ static hash_val_t hash_fun_default(const void *key)
                       +(uint32_t)(((const uint8_t *)(d))[0]) )
 #endif
 
+static int hash_comp_default(const void *key1, const void *key2)
+{
+    return strcmp(key1, key2);
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
 static hash_val_t hash_fun2(const void *key)
 {
     int len, rem;
@@ -897,13 +901,6 @@ static hash_val_t hash_fun2(const void *key)
     return hash;
 }
 
-static int hash_comp_default(const void *key1, const void *key2)
-{
-    return strcmp(key1, key2);
-}
-
-#ifdef KAZLIB_TEST_MAIN
-
 #include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
@@ -985,8 +982,10 @@ int main(void)
         "s                      switch to non-functioning allocator\n"
         "q                      quit";
 
-    if (!h)
+    if (!h) {
         puts("hash_create failed");
+        return 1;
+    }
 
     for (;;) {
         if (prompt)
@@ -1015,6 +1014,7 @@ int main(void)
                 puts("out of memory");
                 free((void *) key);
                 free(val);
+                break;
             }
 
             if (!hash_alloc_insert(h, key, val)) {
index dcac14b6bd40a7f4ae8ada703b2244721d5d5261..886344eb13e3453f26f28b23f48856b2c650e8a4 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
  * $Name:  $
  */
 
index 1c5c4c425e4359e96e3ceea8539d574cbb52ef2c..cd65f7a9003358971df2d7c6566c0892519f28f7 100644 (file)
@@ -38,6 +38,7 @@
 #include "fork.h"
 #include "uam_auth.h"
 #include "afp_zeroconf.h"
+#include "afpstats.h"
 
 #define AFP_LISTENERS 32
 #define FDSET_SAFETY  5
@@ -45,7 +46,7 @@
 unsigned char nologin = 0;
 
 static AFPObj obj;
-static server_child *server_children;
+static server_child_t *server_children;
 static sig_atomic_t reloadconfig = 0;
 static sig_atomic_t gotsigchld = 0;
 
@@ -54,9 +55,8 @@ static struct pollfd *fdset;
 static struct polldata *polldata;
 static int fdset_size;          /* current allocated size */
 static int fdset_used;          /* number of used elements */
-static int disasociated_ipc_fd; /* disasociated sessions uses this fd for IPC */
 
-static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child *server_children);
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children);
 
 static void afp_exit(int ret)
 {
@@ -81,16 +81,6 @@ static void fd_set_listening_sockets(const AFPObj *config)
                      LISTEN_FD,
                      dsi);
     }
-
-    if (config->options.flags & OPTION_KEEPSESSIONS)
-        fdset_add_fd(config->options.connections + AFP_LISTENERS + FDSET_SAFETY,
-                     &fdset,
-                     &polldata,
-                     &fdset_used,
-                     &fdset_size,
-                     disasociated_ipc_fd,
-                     DISASOCIATED_IPC_FD,
-                     NULL);
 }
  
 static void fd_reset_listening_sockets(const AFPObj *config)
@@ -100,9 +90,6 @@ static void fd_reset_listening_sockets(const AFPObj *config)
     for (dsi = config->dsi; dsi; dsi = dsi->next) {
         fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, dsi->serversock);
     }
-
-    if (config->options.flags & OPTION_KEEPSESSIONS)
-        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd);
 }
 
 /* ------------------ */
@@ -112,22 +99,9 @@ static void afp_goaway(int sig)
 
     case SIGTERM:
     case SIGQUIT:
-        switch (sig) {
-        case SIGTERM:
-            LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
-            break;
-        case SIGQUIT:
-            if (obj.options.flags & OPTION_KEEPSESSIONS) {
-                LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT, NOT disconnecting clients");
-            } else {
-                LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT");
-                sig = SIGTERM;
-            }
-            break;
-        }
+        LOG(log_note, logtype_afpd, "AFP Server shutting down");
         if (server_children)
-            server_child_kill(server_children, CHILD_DSIFORK, sig);
-
+            server_child_kill(server_children, SIGTERM);
         _exit(0);
         break;
 
@@ -137,7 +111,7 @@ static void afp_goaway(int sig)
         LOG(log_info, logtype_afpd, "disallowing logins");        
 
         if (server_children)
-            server_child_kill(server_children, CHILD_DSIFORK, sig);
+            server_child_kill(server_children, sig);
         break;
 
     case SIGHUP :
@@ -167,11 +141,9 @@ static void child_handler(void)
 #endif /* ! WAIT_ANY */
 
     while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
-        for (i = 0; i < server_children->nforks; i++) {
-            if ((fd = server_child_remove(server_children, i, pid)) != -1) {
-                fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);        
-                break;
-            }
+        if ((fd = server_child_remove(server_children, pid)) != -1) {
+            fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);        
+            break;
         }
 
         if (WIFEXITED(status)) {
@@ -210,8 +182,6 @@ static int setlimits(void)
 
 int main(int ac, char **av)
 {
-    fd_set              rfds;
-    void                *ipc;
     struct sigaction   sv;
     sigset_t            sigs;
     int                 ret;
@@ -234,7 +204,7 @@ int main(int ac, char **av)
     /* install child handler for asp and dsi. we do this before afp_goaway
      * as afp_goaway references stuff from here. 
      * XXX: this should really be setup after the initial connections. */
-    if (!(server_children = server_child_alloc(obj.options.connections, CHILD_NFORKS))) {
+    if (!(server_children = server_child_alloc(obj.options.connections))) {
         LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
         afp_exit(EXITERR_SYS);
     }
@@ -253,6 +223,13 @@ int main(int ac, char **av)
         afp_exit(EXITERR_SYS);
     }
 #endif
+
+    sv.sa_handler = SIG_IGN;
+    sigemptyset( &sv.sa_mask );
+    if (sigaction(SIGPIPE, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+        afp_exit(EXITERR_SYS);
+    }
     
     sv.sa_handler = afp_goaway; /* handler for all sigs */
 
@@ -335,6 +312,11 @@ int main(int ac, char **av)
     sigaddset(&sigs, SIGCHLD);
 
     pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+#ifdef HAVE_DBUS_GLIB
+    /* Run dbus AFP statics thread */
+    if (obj.options.flags & OPTION_DBUS_AFPSTATS)
+        (void)afpstats_init(server_children);
+#endif
     if (configinit(&obj) != 0) {
         LOG(log_error, logtype_afpd, "main: no servers configured");
         afp_exit(EXITERR_CONF);
@@ -345,12 +327,6 @@ int main(int ac, char **av)
     cnid_init();
     
     /* watch atp, dsi sockets and ipc parent/child file descriptor. */
-
-    if (obj.options.flags & OPTION_KEEPSESSIONS) {
-        LOG(log_note, logtype_afpd, "Activating continuous service");
-        disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
-    }
-
     fd_set_listening_sockets(&obj);
 
     /* set limits */
@@ -382,12 +358,17 @@ int main(int ac, char **av)
 
         if (reloadconfig) {
             nologin++;
-            auth_unload();
+
             fd_reset_listening_sockets(&obj);
 
             LOG(log_info, logtype_afpd, "re-reading configuration file");
 
             configfree(&obj, NULL);
+            afp_config_free(&obj);
+
+            if (afp_config_parse(&obj, "afpd") != 0)
+                afp_exit(EXITERR_CONF);
+
             if (configinit(&obj) != 0) {
                 LOG(log_error, logtype_afpd, "config re-read: no servers configured");
                 afp_exit(EXITERR_CONF);
@@ -416,14 +397,14 @@ int main(int ac, char **av)
                 switch (polldata[i].fdtype) {
 
                 case LISTEN_FD:
-                    if (child = dsi_start(&obj, (DSI *)polldata[i].data, server_children)) {
+                    if ((child = dsi_start(&obj, (DSI *)polldata[i].data, server_children))) {
                         /* Add IPC fd to select fd set */
                         fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
                                      &fdset,
                                      &polldata,
                                      &fdset_used,
                                      &fdset_size,
-                                     child->ipc_fd,
+                                     child->afpch_ipc_fd,
                                      IPC_FD,
                                      child);
                     }
@@ -431,46 +412,13 @@ int main(int ac, char **av)
 
                 case IPC_FD:
                     child = (afp_child_t *)polldata[i].data;
-                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
-
-                    if (ipc_server_read(server_children, child->ipc_fd) != 0) {
-                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fd);
-                        close(child->ipc_fd);
-                        child->ipc_fd = -1;
-                        if ((obj.options.flags & OPTION_KEEPSESSIONS) && child->disasociated) {
-                            LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
-                            server_child_remove(server_children, CHILD_DSIFORK, child->pid);
-                        }
-                    }
-                    break;
-
-                case DISASOCIATED_IPC_FD:
-                    LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
-                    if ((recon_ipc_fd = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
-                        LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
-                        break;
-                    }
-                    if (readt(recon_ipc_fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
-                        LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
-                        close(recon_ipc_fd);
-                        break;
-                    }
-                    LOG(log_note, logtype_afpd, "main: IPC reconnect from pid [%u]", pid);
+                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->afpch_pid);
 
-                    if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, recon_ipc_fd)) == NULL) {
-                        LOG(log_error, logtype_afpd, "main: server_child_add");
-                        close(recon_ipc_fd);
-                        break;
+                    if (ipc_server_read(server_children, child->afpch_ipc_fd) != 0) {
+                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->afpch_ipc_fd);
+                        close(child->afpch_ipc_fd);
+                        child->afpch_ipc_fd = -1;
                     }
-                    child->disasociated = 1;
-                    fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY,
-                                 &fdset,
-                                 &polldata,
-                                 &fdset_used,
-                                 &fdset_size,
-                                 recon_ipc_fd,
-                                 IPC_FD,
-                                 child);
                     break;
 
                 default:
@@ -484,7 +432,7 @@ int main(int ac, char **av)
     return 0;
 }
 
-static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child *server_children)
+static afp_child_t *dsi_start(AFPObj *obj, DSI *dsi, server_child_t *server_children)
 {
     afp_child_t *child = NULL;
 
index dce7e61681ac8e646ad4ed5ef810d39dacade824..18d6fcd91a227526a728f4b2bec9d6a83d7b4142 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: mangle.h,v 1.7 2009-10-13 22:55:37 didg Exp $
  *
  */
 
index b250c16633991d0a6a36a365f1cd48c585220bdb..50440ab42b6d2f38677dad27c86a88ae8299b218 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: nfsquota.c,v 1.13 2009-10-13 22:55:37 didg Exp $
  *
  * parts of this are lifted from the bsd quota program and are
  * therefore under the following copyright:
index 3d690079ba84f7136575afa593e79ad2a3c6425a..cf77d4906ad6edaf0a8dc82c40ba6f75f378be0d 100644 (file)
@@ -228,14 +228,14 @@ struct ofork *of_find(const uint16_t ofrefnum )
 }
 
 /* -------------------------- */
-int of_stat(struct path *path)
+int of_stat(const struct vol *vol, struct path *path)
 {
     int ret;
 
     path->st_errno = 0;
     path->st_valid = 1;
 
-    if ((ret = lstat(path->u_name, &path->st)) < 0) {
+    if ((ret = ostat(path->u_name, &path->st, vol_syml_opt(vol))) < 0) {
         LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
             cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
        path->st_errno = errno;
@@ -274,7 +274,7 @@ int of_statdir(struct vol *vol, struct path *path)
 
     if (*path->m_name) {
         /* not curdir */
-        return of_stat (path);
+        return of_stat(vol, path);
     }
     path->st_errno = 0;
     path->st_valid = 1;
@@ -286,7 +286,7 @@ int of_statdir(struct vol *vol, struct path *path)
 
     LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
 
-    if (!(ret = lstat(pathname, &path->st)))
+    if (!(ret = ostat(pathname, &path->st, vol_syml_opt(vol))))
         return 0;
 
     path->st_errno = errno;
@@ -297,7 +297,7 @@ int of_statdir(struct vol *vol, struct path *path)
            return -1;
        path->st_errno = 0;
 
-       if ((ret = lstat(cfrombstr(path->d_dir->d_u_name), &path->st)) < 0) 
+       if ((ret = ostat(cfrombstr(path->d_dir->d_u_name), &path->st, vol_syml_opt(vol))) < 0) 
            path->st_errno = errno;
     }
 
@@ -305,13 +305,13 @@ int of_statdir(struct vol *vol, struct path *path)
 }
 
 /* -------------------------- */
-struct ofork *of_findname(struct path *path)
+struct ofork *of_findname(const struct vol *vol, struct path *path)
 {
     struct ofork *of;
     struct file_key key;
 
     if (!path->st_valid) {
-        of_stat(path);
+        of_stat(vol, path);
     }
 
     if (path->st_errno)
@@ -407,7 +407,10 @@ int of_closefork(const AFPObj *obj, struct ofork *ofork)
 
     /* Somone has used write_fork, we assume file was changed, register it to file change event api */
     if (ofork->of_flags & AFPFORK_MODIFIED) {
-        fce_register_file_modification(ofork);
+        struct dir *dir =  dirlookup(ofork->of_vol, ofork->of_did);
+        bstring forkpath = bformat("%s/%s", bdata(dir->d_fullpath), of_name(ofork));
+        fce_register(FCE_FILE_MODIFY, bdata(forkpath), NULL, fce_file);
+        bdestroy(forkpath);
     }
 
     ad_unlock(ofork->of_ad, ofork->of_refnum, ofork->of_flags & AFPFORK_ERROR ? 0 : 1);
@@ -441,7 +444,7 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *
     struct ofork        *of;
     struct adouble      *adp;
 
-    if ((of = of_findname(path))) {
+    if ((of = of_findname(vol, path))) {
         adp = of->of_ad;
     } else {
         ad_init(ad, vol);
index 6903a21610ab421bbd3560569ae0e3cb30131d45..614a7f95e74956a6d5881b4b35b0915cf20c3fe4 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: quota.c,v 1.35 2010-04-03 07:11:35 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -26,6 +25,7 @@
 #include <atalk/afp.h>
 #include <atalk/compat.h>
 #include <atalk/unix.h>
+#include <atalk/util.h>
 
 #include "auth.h"
 #include "volume.h"
@@ -370,7 +370,7 @@ mountp( char *file, int *nfs)
     dev_t                      devno;
     static struct mnttab       mnt;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -381,14 +381,14 @@ mountp( char *file, int *nfs)
 
     while ( getmntent( mtab, &mnt ) == 0 ) {
         /* local fs */
-        if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+        if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
             fclose( mtab );
             return mnt.mnt_mountp;
         }
 
         /* check for nfs. we probably should use
          * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
-        if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt.mnt_special, ':')) {
             *nfs = 1;
             fclose( mtab );
@@ -458,7 +458,7 @@ special(char *file, int *nfs)
     struct mntent      *mnt;
     int                found=0;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0 ) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -469,14 +469,14 @@ special(char *file, int *nfs)
 
     while (( mnt = getmntent( mtab )) != NULL ) {
         /* check for local fs */
-        if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+        if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
            found = 1;
            break;
         }
 
         /* check for an nfs mount entry. the alternative is to use
         * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
-        if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt->mnt_fsname, ':')) {
             *nfs = 1;
            found = 1;
index eb5b99b30b970a95f96547e6e87260be38282c9a..ec1025dda3922106a2325290fd9ff59fb7d13ce2 100644 (file)
@@ -95,7 +95,7 @@ static void status_flags(char *data,
 static int status_server(char *data, const char *server, const struct afp_options *options)
 {
     char                *start = data;
-    char                *Obj, *Type, *Zone;
+    char                *Obj;
     char               buf[32];
     uint16_t           status;
     size_t             len;
@@ -364,7 +364,6 @@ static size_t status_directorynames(char *data,
     data += offset;
 
     char *DirectoryNamesCount = data++;
-    char *DirectoryNames = data;
     size_t size = sizeof(uint8_t);
     *DirectoryNamesCount = 0;
 
@@ -441,8 +440,11 @@ static size_t status_directorynames(char *data,
     }
 
     krb5_unparse_name(context, entry.principal, &principal);
+#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
+    krb5_free_keytab_entry_contents(context, &entry);
+#elif defined(HAVE_KRB5_KT_FREE_ENTRY)
     krb5_kt_free_entry(context, &entry);
-
+#endif
     append_directoryname(&data,
                          offset,
                          &size,
@@ -501,7 +503,7 @@ static size_t status_utf8servername(char *data, int *nameoffset,
     uint16_t namelen;
     size_t len;
     char *begin = data;
-    uint16_t offset, status;
+    uint16_t offset;
 
     memcpy(&offset, data + *nameoffset, sizeof(offset));
     offset = ntohs(offset);
@@ -645,7 +647,7 @@ void set_signature(struct afp_options *options) {
     char *servername_conf;
     int header = 0;
     char buf[1024], *p;
-    FILE *fp = NULL, *randomp;
+    FILE *fp = NULL;
     size_t len;
     char *server_tmp;
     
index 2f94ac8885d76dd18c5b3cf6d3d9d2040681fccb..1cc50d01bf038a4f017c530dcf5b7b001d2d5e9f 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: switch.h,v 1.4 2009-10-15 10:43:13 didg Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
index d99d381edd38cc4408a5076039ef47f53d4e7899..208c9ff234313dd8194a7ac9e488b609b6f9ef99 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uam.c,v 1.35 2009-11-08 01:15:31 didg Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved.  See COPYRIGHT.
@@ -31,6 +30,7 @@
 #include <atalk/util.h>
 #include <atalk/globals.h>
 #include <atalk/volume.h>
+#include <atalk/bstrlib.h>
 
 #include "afp_config.h"
 #include "auth.h"
@@ -209,25 +209,24 @@ struct passwd *uam_getname(void *private, char *name, const int len)
         return pwent;
         
     /* if we have a NT domain name try with it */
-    if (obj->options.ntdomain && obj->options.ntseparator) {
+    if (obj->options.addomain || (obj->options.ntdomain && obj->options.ntseparator)) {
         /* FIXME What about charset ? */
-        size_t ulen = strlen(obj->options.ntdomain) + strlen(obj->options.ntseparator) + strlen(name);
-        if ((p = malloc(ulen +1))) {
-            strcpy(p, obj->options.ntdomain);
-            strcat(p, obj->options.ntseparator);
-            strcat(p, name);
-            pwent = getpwnam(p);
-            free(p);
-            if (pwent) {
-                int len = strlen(pwent->pw_name);              
-                if (len < MAXUSERLEN) {
-                    strncpy(name,pwent->pw_name, MAXUSERLEN);  
-                }else{
-                    LOG(log_error, logtype_uams, "MAJOR:The name %s is longer than %d",pwent->pw_name,MAXUSERLEN);
-                }
-
-                return pwent;
+        bstring princ;
+        if (obj->options.addomain)
+            princ = bformat("%s@%s", name, obj->options.addomain);
+        else
+            princ = bformat("%s%s%s", obj->options.ntdomain, obj->options.ntseparator, name);
+        pwent = getpwnam(bdata(princ));
+        bdestroy(princ);
+
+        if (pwent) {
+            int len = strlen(pwent->pw_name);              
+            if (len < MAXUSERLEN) {
+                strncpy(name,pwent->pw_name, MAXUSERLEN);  
+            } else {
+                LOG(log_error, logtype_uams, "The name '%s' is longer than %d", pwent->pw_name, MAXUSERLEN);
             }
+            return pwent;
         }
     }
 #ifndef NO_REAL_USER_NAME
index e26e6d1f1b50f702da0fb3ea0d9786937b275e99..a3e4d59704592e9626734bdde323a992953241b1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uid.c,v 1.14 2005-04-28 20:49:45 bfernhomberg Exp $
  * code: jeff@univrel.pr.uconn.edu
  *
  * These functions are abstracted here, so that all calls for resolving
index 647cbd8d164a76182953c420c3650ae773f2a2a2..87745147fc3c0a4bea82ba5652acf4bfbdca107c 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uid.h,v 1.6 2002-08-30 19:32:41 didg Exp $
  * code: jeff@univrel.pr.uconn.edu
  */
 
index 16562f3b757b1d0912d09e221a2d52b5144aee33..7c90462874cd0cceec47f3c9836f42319e6a1937 100644 (file)
@@ -158,7 +158,7 @@ void accessmode(const AFPObj *obj, const struct vol *vol, char *path, struct mac
 
     ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
     if (!st) {
-        if (lstat(path, &sb) != 0)
+        if (ostat(path, &sb, vol_syml_opt(vol)) != 0)
             return;
         st = &sb;
     }
@@ -207,7 +207,7 @@ mode_t mtoumode(struct maccess *ma)
 int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
 {
     if (!path->st_valid) {
-        of_stat(path);
+        of_stat(vol, path);
     }
 
     if (path->st_errno) {
@@ -216,7 +216,7 @@ int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
         
     mode |= vol->v_fperm;
 
-    if (setfilmode( path->u_name, mode, &path->st, vol->v_umask) < 0)
+    if (setfilmode(vol, path->u_name, mode, &path->st) < 0)
         return -1;
     /* we need to set write perm if read set for resource fork */
     return vol->vfs->vfs_setfilmode(vol, path->u_name, mode, &path->st);
@@ -224,7 +224,7 @@ int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
 
 
 /* --------------------- */
-int setdirunixmode(const struct vol *vol, const char *name, mode_t mode)
+int setdirunixmode(const struct vol *vol, char *name, mode_t mode)
 {
     LOG(log_debug, logtype_afpd, "setdirunixmode('%s', mode:%04o) {v_dperm:%04o}",
         fullpathname(name), mode, vol->v_dperm);
@@ -249,7 +249,7 @@ int setdirunixmode(const struct vol *vol, const char *name, mode_t mode)
 /* ----------------------------- */
 int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct path* path)
 {
-    if (lchown(path->u_name, uid, gid) < 0 && errno != EPERM) {
+    if (ochown( path->u_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
         LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
             uid, gid, path->u_name, strerror(errno));
         return -1;
@@ -271,7 +271,7 @@ int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct
  * co-opting some bits. */
 int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const gid_t gid)
 {
-    if (lchown(name, uid, gid ) < 0 && errno != EPERM ) {
+    if (ochown(name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
             uid, gid, fullpathname(name), strerror(errno) );
     }
@@ -281,4 +281,3 @@ int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const
 
     return( 0 );
 }
-
index 73a515b2dcf357496ffec0ae57aa67ca11f13422..3b3c281b528c57dd4c035243796340823c35ec8f 100644 (file)
@@ -213,7 +213,7 @@ extern int uquota_getvolspace (const AFPObj *obj, struct vol *, VolSpace *, VolS
 
 extern struct afp_options default_options;
 
-extern int setdirunixmode   (const struct vol *, const char *, mode_t);
+extern int setdirunixmode   (const struct vol *, char *, mode_t);
 extern int setdirmode       (const struct vol *, const char *, mode_t);
 extern int setdirowner      (const struct vol *, const char *, const uid_t, const gid_t);
 extern int setfilunixmode   (const struct vol *, struct path*, const mode_t);
index da57a30a822491746f603ba6112516b3ac325dac..205383bb0ee7d939da4d24ba59d7980ba27be109 100644 (file)
@@ -39,6 +39,7 @@
 #include <atalk/iniparser.h>
 #include <atalk/unix.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/server_ipc.h>
 
 #ifdef CNID_DB
 #include <atalk/cnid.h>
@@ -152,7 +153,6 @@ static int get_tm_used(struct vol * restrict vol)
     DIR *dir = NULL;
     const struct dirent *entry;
     const char *p;
-    struct stat st;
     long int links;
     time_t now = time(NULL);
 
@@ -177,13 +177,18 @@ static int get_tm_used(struct vol * restrict vol)
 
             EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") );
             
-            if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1)
+            if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1) {
+                bdestroy(infoplist);
                 continue;
+            }
 
             EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") );
 
-            if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1)
+            if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1) {
+                bdestroy(infoplist);
+                bdestroy(bandsdir);
                 continue;
+            }
 
             used += (links - 1) * bandsize;
             LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes",
@@ -212,7 +217,6 @@ static int getvolspace(const AFPObj *obj, struct vol *vol,
 {
     int         spaceflag, rc;
     uint32_t   maxsize;
-    VolSpace    used;
 #ifndef NO_QUOTA_SUPPORT
     VolSpace    qfree, qtotal;
 #endif
@@ -264,22 +268,6 @@ getvolspace_done:
     return( AFP_OK );
 }
 
-#define FCE_TM_DELTA 10  /* send notification every 10 seconds */
-void vol_fce_tm_event(void)
-{
-    static time_t last;
-    time_t now = time(NULL);
-    struct vol  *vol = getvolumes();
-
-    if ((last + FCE_TM_DELTA) < now) {
-        last = now;
-        for ( ; vol; vol = vol->v_next ) {
-            if (vol->v_flags & AFPVOL_TM)
-                (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_appended);
-        }
-    }
-}
-
 /* -----------------------
  * set volume creation date
  * avoid duplicate, well at least it tries
@@ -543,7 +531,7 @@ int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf
     int         vcnt;
     size_t      len;
 
-    load_volumes(obj, closevol);
+    load_volumes(obj);
 
     data = rbuf + 5;
     for ( vcnt = 0, volume = getvolumes(); volume; volume = volume->v_next ) {
@@ -672,6 +660,31 @@ static int volume_openDB(const AFPObj *obj, struct vol *volume)
     return (!volume->v_cdb)?-1:0;
 }
 
+/*
+ * Send list of open volumes to afpd master via IPC
+ */
+static void server_ipc_volumes(AFPObj *obj)
+{
+    struct vol *volume, *vols;
+    volume = vols = getvolumes();
+    bstring openvolnames = bfromcstr("");
+    bool firstvol = true;
+
+    while (volume) {
+        if (volume->v_flags & AFPVOL_OPEN) {
+            if (!firstvol)
+                bcatcstr(openvolnames, ", ");
+            else
+                firstvol = false;
+            bcatcstr(openvolnames, volume->v_localname);
+        }
+        volume = volume->v_next;
+    }
+
+    ipc_child_write(obj->ipc_fd, IPC_VOLUMES, blength(openvolnames), bdata(openvolnames));
+    bdestroy(openvolnames);
+}
+
 /* -------------------------
  * we are the user here
  */
@@ -679,16 +692,14 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
 {
     struct stat st;
     char    *volname;
-    char        *p;
 
     struct vol  *volume;
     struct dir  *dir;
     int     len, ret;
     size_t  namelen;
     uint16_t   bitmap;
-    char        path[ MAXPATHLEN + 1];
     char        *vol_uname;
-    char        *vol_mname;
+    char        *vol_mname = NULL;
     char        *volname_tmp;
 
     ibuf += 2;
@@ -721,7 +732,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     if ((len + 1) & 1) /* pad to an even boundary */
         ibuf++;
 
-    load_volumes(obj, closevol);
+    load_volumes(obj);
 
     for ( volume = getvolumes(); volume; volume = volume->v_next ) {
         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
@@ -765,33 +776,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         return AFPERR_PARAM;
     }
 
-    if ( NULL == getcwd(path, MAXPATHLEN)) {
-        /* shouldn't be fatal but it will fail later */
-        LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
-        return AFPERR_MISC;
-    }
-
-    /* Normalize volume path */
-#ifdef REALPATH_TAKES_NULL
-    if ((volume->v_path = realpath(path, NULL)) == NULL)
-        return AFPERR_MISC;
-#else
-    if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
-        return AFPERR_MISC;
-    if (realpath(path, volume->v_path) == NULL) {
-        free(volume->v_path);
-        return AFPERR_MISC;
-    }
-    /* Safe some memory */
-    char *tmp;
-    if ((tmp = strdup(volume->v_path)) == NULL) {
-        free(volume->v_path);
-        return AFPERR_MISC;
-    }
-    free(volume->v_path);
-    volume->v_path = tmp;
-#endif
-
     if (volume_codepage(obj, volume) < 0) {
         ret = AFPERR_MISC;
         goto openvol_err;
@@ -820,9 +804,9 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         goto openvol_err;
     }
 
-    if ((vol_uname = strrchr(path, '/')) == NULL)
-        vol_uname = path;
-    else if (*(vol_uname + 1) != '\0')
+    if ((vol_uname = strrchr(volume->v_path, '/')) == NULL)
+        vol_uname = volume->v_path;
+    else if (vol_uname[1] != '\0')
         vol_uname++;
 
     if ((dir = dir_new(vol_mname,
@@ -838,7 +822,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         ret = AFPERR_MISC;
         goto openvol_err;
     }
-    free(vol_mname);
     volume->v_root = dir;
     curdir = dir;
 
@@ -876,6 +859,8 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         if ((msg = iniparser_getstring(obj->iniconfig, volume->v_configname, "login message",  NULL)) != NULL)
             setmessage(msg);
 
+        free(vol_mname);
+        server_ipc_volumes(obj);
         return( AFP_OK );
     }
 
@@ -890,6 +875,7 @@ openvol_err:
         cnid_close(volume->v_cdb);
         volume->v_cdb = NULL;
     }
+    free(vol_mname);
     *rbuflen = 0;
     return ret;
 }
@@ -947,6 +933,7 @@ int afp_closevol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
     (void)chdir("/");
     curdir = NULL;
     closevol(obj, vol);
+    server_ipc_volumes(obj);
 
     return( AFP_OK );
 }
index 3bca88214bfee50687bc7dc052d5e70a9bb8c948..a8bd2b8bad453a6b51de78a8f90dad5ee8026257 100644 (file)
@@ -19,7 +19,6 @@ extern int              ustatfs_getvolspace (const struct vol *,
                                              uint32_t *);
 extern void             setvoltime (AFPObj *, struct vol *);
 extern int              pollvoltime (AFPObj *);
-extern void             vol_fce_tm_event(void);
 
 /* FP functions */
 int afp_openvol      (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 8f8b734216fb2a9413d1ffde668d2ff5fb40b5f1..31d74e6c8b06724e6e1e1102393fa66cd1d18a69 100644 (file)
 #include <errno.h>
 
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
 #include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/util.h>
+#include <atalk/errchk.h>
 
 #include "cmd_dbd.h"
-#include "dbd.h"
-#include "dbif.h"
-#include "db_param.h"
-#include "pack.h"
 
-#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
+enum dbd_cmd {dbd_scan, dbd_rebuild};
 
-int nocniddb = 0;               /* Dont open CNID database, only scan filesystem */
+/* Global variables */
 volatile sig_atomic_t alarmed;  /* flags for signals */
-int db_locked;                  /* have we got the fcntl lock on lockfile ? */
-
-static DBD *dbd;
-static int verbose;             /* Logging flag */
-static int exclusive;           /* Exclusive volume access */
-static struct db_param db_param = {
-    NULL,                       /* Volume dirpath */
-    1,                          /* bdb logfile autoremove */
-    64 * 1024,                  /* bdb cachesize (64 MB) */
-    DEFAULT_MAXLOCKS,           /* maxlocks */
-    DEFAULT_MAXLOCKOBJS,        /* maxlockobjs */
-    0,                          /* flush_interval */
-    0,                          /* flush_frequency */
-    0,                          /* usock_file */
-    -1,                         /* fd_table_size */
-    -1,                         /* idle_timeout */
-    -1                          /* max_vols */
-};
-static char dbpath[MAXPATHLEN+1];   /* Path to the dbd database */
 
-/* 
-   Provide some logging
- */
-void dbd_log(enum logtype lt, char *fmt, ...)
-{
-    int len;
-    static char logbuffer[1024];
-    va_list args;
+/* Local variables */
+static dbd_flags_t flags;
 
-    if ( (lt == LOGSTD) || (verbose == 1)) {
-        va_start(args, fmt);
-        len = vsnprintf(logbuffer, 1023, fmt, args);
-        va_end(args);
-        logbuffer[1023] = 0;
+/***************************************************************************
+ * Local functions
+ ***************************************************************************/
 
-        printf("%s\n", logbuffer);
-    }
-}
-
-/* 
-   SIGNAL handling:
-   catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+/*
+ * SIGNAL handling:
+ * catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
  */
-
 static void sig_handler(int signo)
 {
     alarmed = 1;
@@ -128,99 +92,79 @@ static void set_signal(void)
 
 static void usage (void)
 {
-    printf("dbd (Netatalk %s)\n"
-           "Usage: dbd [-CeFtvx] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u <path to netatalk volume>\n"
-           "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n"
-           "dbd must be run with appropiate permissions i.e. as root.\n\n"
-           "Main commands are:\n"
-           "   -d Dump CNID database\n"
-           "      Option: -i dump indexes too\n\n"
-           "   -s Scan volume:\n"
-           "      Options: -c Don't check .AppleDouble stuff, only ckeck orphaned.\n"
-           "               -n Don't open CNID database, skip CNID checks.\n\n"
-           "   -r Rebuild volume:\n"
-           "      Options: -c Don't create .AppleDouble stuff, only cleanup orphaned.\n"
-           "               -f wipe database and rebuild from IDs stored in AppleDouble\n"
-           "                  metadata file or EA. Implies -e.\n\n"
-           "   -u Upgrade:\n"
-           "      Opens the database which triggers any necessary upgrades,\n"
-           "      then closes and exits.\n\n"
-           "General options:\n"
-           "   -C convert from adouble:v2 to adouble:ea (use with -r)\n"
+    printf("Usage: dbd [-cfFstvV] <path to netatalk volume>\n\n"
+           "dbd scans all file and directories of AFP volumes, updating the\n"
+           "CNID database of the volume. dbd must be run with appropiate\n"
+           "permissions i.e. as root.\n\n"
+           "Options:\n"
+           "   -s scan volume: treat the volume as read only and don't\n"
+           "      perform any filesystem modifications\n"
+           "   -c convert from adouble:v2 to adouble:ea\n"
            "   -F location of the afp.conf config file\n"
-           "   -e only work on inactive volumes and lock them (exclusive)\n"
-           "   -x rebuild indexes (just for completeness, mostly useless!)\n"
+           "   -f delete and recreate CNID database\n"
            "   -t show statistics while running\n"
-           "   -v verbose\n\n"
-           , VERSION
+           "   -v verbose\n"
+           "   -V show version info\n\n"
         );
 }
 
-int main(int argc, char **argv)
+/***************************************************************************
+ * Global functions
+ ***************************************************************************/
+
+void dbd_log(enum logtype lt, char *fmt, ...)
 {
-    int c, lockfd, ret = -1;
-    int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0;
-    dbd_flags_t flags = 0;
-    char *volpath;
-    int cdir;
-    AFPObj obj = { 0 };
-    struct vol *vol;
+    int len;
+    static char logbuffer[1024];
+    va_list args;
 
-    if (geteuid() != 0) {
-        usage();
-        exit(EXIT_FAILURE);
+    if ( (lt == LOGSTD) || (flags & DBD_FLAGS_VERBOSE)) {
+        va_start(args, fmt);
+        len = vsnprintf(logbuffer, 1023, fmt, args);
+        va_end(args);
+        logbuffer[1023] = 0;
+
+        printf("%s\n", logbuffer);
     }
-    /* Inhereting perms in ad_mkdir etc requires this */
-    ad_setfuid(0);
+}
 
-    while ((c = getopt(argc, argv, ":cCdefFinrstuvx")) != -1) {
+int main(int argc, char **argv)
+{
+    EC_INIT;
+    int dbd_cmd = dbd_rebuild;
+    int cdir = -1;
+    AFPObj obj = { 0 };
+    struct vol *vol = NULL;
+    const char *volpath = NULL;
+
+    int c;
+    while ((c = getopt(argc, argv, ":cfF:rstvV")) != -1) {
         switch(c) {
         case 'c':
-            flags |= DBD_FLAGS_CLEANUP;
-            break;
-        case 'C':
             flags |= DBD_FLAGS_V2TOEA;
             break;
-        case 'd':
-            dump = 1;
+        case 'f':
+            flags |= DBD_FLAGS_FORCE;
+            break;
+        case 'F':
+            obj.cmdlineconfigfile = strdup(optarg);
             break;
-        case 'i':
-            dumpindexes = 1;
+        case 'r':
+            /* the default */
             break;
         case 's':
-            scan = 1;
+            dbd_cmd = dbd_scan;
             flags |= DBD_FLAGS_SCAN;
             break;
-        case 'n':
-            nocniddb = 1; /* FIXME: this could/should be a flag too for consistency */
-            break;
-        case 'r':
-            rebuild = 1;
-            break;
         case 't':
             flags |= DBD_FLAGS_STATS;
             break;
-        case 'u':
-            prep_upgrade = 1;
-            break;
         case 'v':
-            verbose = 1;
-            break;
-        case 'e':
-            exclusive = 1;
-            flags |= DBD_FLAGS_EXCL;
-            break;
-        case 'x':
-            rebuildindexes = 1;
-            break;
-        case 'f':
-            force = 1;
-            exclusive = 1;
-            flags |= DBD_FLAGS_FORCE | DBD_FLAGS_EXCL;
-            break;
-        case 'F':
-            obj.cmdlineconfigfile = strdup(optarg);
+            flags |= DBD_FLAGS_VERBOSE;
             break;
+        case 'V':
+            printf("dbd %s\n", VERSION);
+            exit(0);
         case ':':
         case '?':
             usage();
@@ -229,16 +173,18 @@ int main(int argc, char **argv)
         }
     }
 
-    if ((dump + scan + rebuild + prep_upgrade) != 1) {
+    if ( (optind + 1) != argc ) {
         usage();
         exit(EXIT_FAILURE);
     }
+    volpath = argv[optind];
 
-    if ( (optind + 1) != argc ) {
+    if (geteuid() != 0) {
         usage();
         exit(EXIT_FAILURE);
     }
-    volpath = argv[optind];
+    /* Inhereting perms in ad_mkdir etc requires this */
+    ad_setfuid(0);
 
     setvbuf(stdout, (char *) NULL, _IONBF, 0);
 
@@ -257,13 +203,16 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);
     }
 
+    /* Initialize CNID subsystem */
+    cnid_init();
+
     /* Setup logging. Should be portable among *NIXes */
-    if (!verbose)
-        setuplog("default:info", "/dev/tty");
+    if (flags & DBD_FLAGS_VERBOSE)
+        setuplog("default:note, cnid:debug", "/dev/tty");
     else
-        setuplog("default:debug", "/dev/tty");
+        setuplog("default:note", "/dev/tty");
 
-    if (load_volumes(&obj, NULL) != 0) {
+    if (load_volumes(&obj) != 0) {
         dbd_log( LOGSTD, "Couldn't load volumes");
         exit(EXIT_FAILURE);
     }
@@ -278,7 +227,20 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);
     }
 
-    pack_setvol(vol);
+    /* open volume */
+    if (STRCMP(vol->v_cnidscheme, != , "dbd")) {
+        dbd_log(LOGSTD, "\"%s\" isn't a \"dbd\" CNID volume", vol->v_path);
+        exit(EXIT_FAILURE);
+    }
+    if ((vol->v_cdb = cnid_open(vol->v_path,
+                                0000,
+                                "dbd",
+                                vol->v_flags & AFPVOL_NODEV ? CNID_FLAG_NODEV : 0,
+                                vol->v_cnidserver,
+                                vol->v_cnidport)) == NULL) {
+        dbd_log(LOGSTD, "Cant initialize CNID database connection for %s", vol->v_path);
+        exit(EXIT_FAILURE);
+    }
 
     if (vol->v_adouble == AD_VERSION_EA)
         dbd_log( LOGDEBUG, "adouble:ea volume");
@@ -301,145 +263,28 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);        
     }
 
-    /* Enuser dbpath is there, create if necessary */
-    struct stat st;
-    if (stat(vol->v_dbpath, &st) != 0) {
-        if (errno != ENOENT) {
-            dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", vol->v_dbpath, strerror(errno));
-            exit(EXIT_FAILURE);        
-        }
-        if ((mkdir(vol->v_dbpath, 0755)) != 0) {
-            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", vol->v_dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }        
-    }
-
-    /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
-    if ( (strlen(vol->v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
-        dbd_log( LOGSTD, "Volume pathname too long");
-        exit(EXIT_FAILURE);        
-    }
-    strncpy(dbpath, vol->v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
-    strcat(dbpath, "/.AppleDB");
-
-    /* Check or create dbpath */
-    int dbdirfd = open(dbpath, O_RDONLY);
-    if (dbdirfd == -1 && errno == ENOENT) {
-        if (errno == ENOENT) {
-            if ((mkdir(dbpath, 0755)) != 0) {
-                dbd_log( LOGSTD, "Can't create .AppleDB for \"%s\": %s", dbpath, strerror(errno));
-                exit(EXIT_FAILURE);
-            }
-        } else {
-            dbd_log( LOGSTD, "Somethings wrong with .AppleDB for \"%s\", giving up: %s", dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-    } else {
-        close(dbdirfd);
-    }
-
-    /* Get db lock */
-    if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
-        goto exit_noenv;
-    if (db_locked != LOCK_EXCL) {
-        dbd_log(LOGDEBUG, "Database is in use, acquiring shared lock");
-        /* Couldn't get exclusive lock, try shared lock if -e wasn't requested */
-        if (exclusive) {
-            dbd_log(LOGSTD, "Database is in use and exlusive was requested");
-            goto exit_noenv;
+    if (flags & DBD_FLAGS_FORCE) {
+        if (cnid_wipe(vol->v_cdb) != 0) {
+            dbd_log( LOGSTD, "Failed to wipe CNID db");
+            EC_FAIL;
         }
-        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD)
-            goto exit_noenv;
-    }
-
-    /* Check if -f is requested and wipe db if yes */
-    if ((flags & DBD_FLAGS_FORCE) && rebuild) {
-        char cmd[8 + MAXPATHLEN];
-        if ((db_locked = get_lock(LOCK_FREE, NULL)) != 0)
-            goto exit_noenv;
-
-        snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
-        dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
-        system(cmd);
-        if ((mkdir(dbpath, 0755)) != 0) {
-            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        dbd_log( LOGDEBUG, "Removed old database.");
-        if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
-            goto exit_noenv;
-    }
-
-    /* 
-       Lets start with the BerkeleyDB stuff
-    */
-    if ( ! nocniddb) {
-        if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL)
-            goto exit_noenv;
-        
-        if (dbif_env_open(dbd,
-                          &db_param,
-                          (db_locked == LOCK_EXCL) ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
-            dbd_log( LOGSTD, "error opening database!");
-            goto exit_noenv;
-        }
-
-        if (db_locked == LOCK_EXCL)
-            dbd_log( LOGDEBUG, "Finished recovery.");
-
-        if (dbif_open(dbd, NULL, rebuildindexes) < 0) {
-            dbif_close(dbd);
-            goto exit_failure;
-        }
-
-        /* Prepare upgrade ? We're done */
-        if (prep_upgrade) {
-            (void)dbif_txn_close(dbd, 1);
-            goto cleanup;
-        }
-    }
-
-    /* Downgrade db lock if not running exclusive */
-    if (!exclusive && (db_locked == LOCK_EXCL)) {
-        if (get_lock(LOCK_UNLOCK, NULL) != 0)
-            goto exit_failure;
-        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD)
-            goto exit_failure;
     }
 
     /* Now execute given command scan|rebuild|dump */
-    if (dump && ! nocniddb) {
-        if (dbif_dump(dbd, dumpindexes) < 0) {
-            dbd_log( LOGSTD, "Error dumping database");
-        }
-    } else if ((rebuild && ! nocniddb) || scan) {
-        if (cmd_dbd_scanvol(dbd, vol, flags) < 0) {
+    switch (dbd_cmd) {
+    case dbd_scan:
+    case dbd_rebuild:
+        if (cmd_dbd_scanvol(vol, flags) < 0) {
             dbd_log( LOGSTD, "Error repairing database.");
         }
+        break;
     }
 
-cleanup:
-    /* Cleanup */
-    dbd_log(LOGDEBUG, "Closing db");
-    if (! nocniddb) {
-        if (dbif_close(dbd) < 0) {
-            dbd_log( LOGSTD, "Error closing database");
-            goto exit_failure;
-        }
-    }
-
-exit_success:
-    ret = 0;
-
-exit_failure:
-    if (dbif_env_remove(dbpath) < 0) {
-        dbd_log( LOGSTD, "Error removing BerkeleyDB database environment");
-        ret++;
-    }
-    get_lock(0, NULL);
+EC_CLEANUP:
+    if (vol)
+        cnid_close(vol->v_cdb);
 
-exit_noenv:    
-    if ((fchdir(cdir)) < 0)
+    if (cdir != -1 && (fchdir(cdir) < 0))
         dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
 
     if (ret == 0)
index efec636b482e464f40a1bda7ebc469dd420acb70..9fcb3f4edbef4178feb1004a7864bab7cfd520c4 100644 (file)
@@ -12,29 +12,18 @@ typedef unsigned int dbd_flags_t;
 
 #define DBD_FLAGS_SCAN     (1 << 0)
 #define DBD_FLAGS_FORCE    (1 << 1)
-#define DBD_FLAGS_EXCL     (1 << 2)
-#define DBD_FLAGS_CLEANUP  (1 << 3) /* Dont create AD stuff, but cleanup orphaned */
-#define DBD_FLAGS_STATS    (1 << 4)
-#define DBD_FLAGS_V2TOEA   (1 << 5) /* Convert adouble:v2 to adouble:ea */
+#define DBD_FLAGS_STATS    (1 << 2)
+#define DBD_FLAGS_V2TOEA   (1 << 3) /* Convert adouble:v2 to adouble:ea */
+#define DBD_FLAGS_VERBOSE  (1 << 4)
 
 #define ADv2_DIRNAME ".AppleDouble"
 
 #define DIR_DOT_OR_DOTDOT(a) \
         ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
 
-#define STRCMP(a,b,c) \
-        (strcmp(a,c) b 0)
-
-extern int nocniddb; /* Dont open CNID database, only scan filesystem */
-extern int db_locked; /* have we got the fcntl lock on lockfd ? */
 extern volatile sig_atomic_t alarmed;
 
 extern void dbd_log(enum logtype lt, char *fmt, ...);
-extern int cmd_dbd_scanvol(DBD *dbd, struct vol *vol, dbd_flags_t flags);
+extern int cmd_dbd_scanvol(struct vol *vol, dbd_flags_t flags);
 
-/*
-  Functions for querying the database which couldn't be reused from the existing
-  funcs pool of dbd_* for one reason or another
-*/
-extern int cmd_dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
 #endif /* CMD_DBD_H */
index dcbd4c74779161acb4be4f3b3feda12bc6bec771..b9fbf3e88ac4d63bea0766caa4fef714b522ffee 100644 (file)
 #include <atalk/adouble.h>
 #include <atalk/unicode.h>
 #include <atalk/netatalk_conf.h>
-#include <atalk/cnid_dbd_private.h>
 #include <atalk/volume.h>
 #include <atalk/ea.h>
 #include <atalk/util.h>
 #include <atalk/acl.h>
 #include <atalk/compat.h>
+#include <atalk/cnid.h>
+#include <atalk/errchk.h>
 
 #include "cmd_dbd.h"
 #include "dbif.h"
 #define ADFILE_OK (adfile_ok == 0)
 
 
-static struct vol     *myvol;
 static char           cwdbuf[MAXPATHLEN+1];
-static DBD            *dbd;
-static DBD            *dbd_rebuild;
+static struct vol     *vol;
 static dbd_flags_t    dbd_flags;
 static char           stamp[CNID_DEV_LEN];
 static char           *netatalk_dirs[] = {
@@ -83,74 +82,28 @@ static char *utompath(char *upath)
     u = upath;
     outlen = strlen(upath);
 
-    if ((myvol->v_casefold & AFPVOL_UTOMUPPER))
+    if ((vol->v_casefold & AFPVOL_UTOMUPPER))
         flags |= CONV_TOUPPER;
-    else if ((myvol->v_casefold & AFPVOL_UTOMLOWER))
+    else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
         flags |= CONV_TOLOWER;
 
-    if ((myvol->v_flags & AFPVOL_EILSEQ)) {
+    if ((vol->v_flags & AFPVOL_EILSEQ)) {
         flags |= CONV__EILSEQ;
     }
 
     /* convert charsets */
-    if ((size_t)-1 == ( outlen = convert_charset(myvol->v_volcharset,
+    if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset,
                                                  CH_UTF8_MAC,
-                                                 myvol->v_maccharset,
+                                                 vol->v_maccharset,
                                                  u, outlen, mpath, MAXPATHLEN, &flags)) ) {
         dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
-                 myvol->v_volcodepage, myvol->v_maccodepage, u);
+                 vol->v_volcodepage, vol->v_maccodepage, u);
         return NULL;
     }
 
     return(m);
 }
 
-/*
-  Taken form afpd/desktop.c
-*/
-static char *mtoupath(char *mpath)
-{
-    static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
-    char    *m, *u;
-    size_t       inplen;
-    size_t       outlen;
-    u_int16_t    flags = 0;
-
-    if (!mpath)
-        return NULL;
-
-    if ( *mpath == '\0' ) {
-        return( "." );
-    }
-
-    /* set conversion flags */
-    if ((myvol->v_casefold & AFPVOL_MTOUUPPER))
-        flags |= CONV_TOUPPER;
-    else if ((myvol->v_casefold & AFPVOL_MTOULOWER))
-        flags |= CONV_TOLOWER;
-
-    if ((myvol->v_flags & AFPVOL_EILSEQ)) {
-        flags |= CONV__EILSEQ;
-    }
-
-    m = mpath;
-    u = upath;
-
-    inplen = strlen(m);
-    outlen = MAXPATHLEN;
-
-    if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
-                                                myvol->v_volcharset,
-                                                myvol->v_maccharset,
-                                                m, inplen, u, outlen, &flags)) ) {
-        dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
-                 myvol->v_volcodepage, mpath);
-        return NULL;
-    }
-
-    return( upath );
-}
-
 /*
   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
   Returns pointer to name or NULL.
@@ -186,46 +139,15 @@ static const char *check_special_dirs(const char *name)
  */
 static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname)
 {
-    int ret;
     cnid_t id;
 
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = 0;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = sp->st_dev;
-    rqst.ino = sp->st_ino;
-    rqst.type = S_ISDIR(sp->st_mode) ? 1 : 0;
-    rqst.name = (char *)oldname;
-    rqst.namelen = strlen(oldname);
-
     /* Query the database */
-    ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
-    if (dbif_txn_close(dbd, ret) != 0)
-        return -1;
-    if (rply.result != CNID_DBD_RES_OK)
+    if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID)
+        /* not in db, no need to update */
         return 0;
-    id = rply.cnid;
-
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = id;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = sp->st_dev;
-    rqst.ino = sp->st_ino;
-    rqst.type = S_ISDIR(sp->st_mode) ? 1 : 0;
-    rqst.name = (char *)newname;
-    rqst.namelen = strlen(newname);
 
     /* Update the database */
-    ret = dbd_update(dbd, &rqst, &rply);
-    if (dbif_txn_close(dbd, ret) != 0)
-        return -1;
-    if (rply.result != CNID_DBD_RES_OK)
+    if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0)
         return -1;
 
     return 0;
@@ -243,10 +165,10 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
 
     *newname = NULL;
 
-    if (myvol->v_adouble == AD_VERSION_EA) {
+    if (vol->v_adouble == AD_VERSION_EA) {
         if (!(dbd_flags & DBD_FLAGS_V2TOEA))
             return 0;
-        if (ad_convert(fname, st, myvol, newname) != 0) {
+        if (ad_convert(fname, st, vol, newname) != 0) {
             switch (errno) {
             case ENOENT:
                 break;
@@ -258,13 +180,10 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
         return 0;
     }
     
-    if (dbd_flags & DBD_FLAGS_CLEANUP)
-        return 0;
-
     if (S_ISDIR(st->st_mode))
         adflags |= ADFLAGS_DIR;
 
-    adname = myvol->ad_path(fname, adflags);
+    adname = vol->ad_path(fname, adflags);
 
     if ((ret = access( adname, F_OK)) != 0) {
         if (errno != ENOENT) {
@@ -280,7 +199,7 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
             return -1;
 
         /* Create ad file */
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
 
         if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
@@ -300,7 +219,7 @@ static int check_adfile(const char *fname, const struct stat *st, const char **n
         chmod(adname, st->st_mode);
 #endif
     } else {
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
         if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) {
             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
             return -1;
@@ -369,7 +288,7 @@ static int check_eafiles(const char *fname)
     struct stat st;
     char *eaname;
 
-    if ((ret = ea_open(myvol, fname, EA_RDWR, &ea)) != 0) {
+    if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) {
         if (errno == ENOENT)
             return 0;
         dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
@@ -422,10 +341,7 @@ static int check_addir(int volroot)
     struct adouble ad;
     char *mname = NULL;
 
-    if (dbd_flags & DBD_FLAGS_CLEANUP)
-        return 0;
-
-    if (myvol->v_adouble == AD_VERSION_EA)
+    if (vol->v_adouble == AD_VERSION_EA)
         return 0;
 
     /* Check for ad-dir */
@@ -438,10 +354,10 @@ static int check_addir(int volroot)
     }
 
     /* Check for ".Parent" */
-    if ( (adpar_ok = access(myvol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
+    if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
         if (errno != ENOENT) {
             dbd_log(LOGSTD, "Access error on '%s/%s': %s",
-                    cwdbuf, myvol->ad_path(".", ADFLAGS_DIR), strerror(errno));
+                    cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno));
             return -1;
         }
         dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
@@ -460,7 +376,7 @@ static int check_addir(int volroot)
         }
 
         /* Create ad dir and set name */
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
 
         if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
@@ -481,7 +397,7 @@ static int check_addir(int volroot)
             return -1;
         }
         chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
-        chown(myvol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
+        chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
     }
 
     return 0;
@@ -500,7 +416,7 @@ static int check_eafile_in_adouble(const char *name)
     char *namep, *namedup = NULL;
 
     /* Check if this is an AFPVOL_EA_AD vol */
-    if (myvol->v_vfs_ea == AFPVOL_EA_AD) {
+    if (vol->v_vfs_ea == AFPVOL_EA_AD) {
         /* Does the filename contain "::EA" ? */
         namedup = strdup(name);
         if ((namep = strstr(namedup, "::EA")) == NULL) {
@@ -559,7 +475,7 @@ static int read_addir(void)
     struct stat st;
 
     if ((chdir(ADv2_DIRNAME)) != 0) {
-        if (myvol->v_adouble == AD_VERSION_EA) {
+        if (vol->v_adouble == AD_VERSION_EA) {
             return 0;
         }
         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
@@ -643,49 +559,27 @@ static int read_addir(void)
 */
 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok)
 {
-    int ret, adflags = ADFLAGS_HF;
+    int adflags = ADFLAGS_HF;
     cnid_t db_cnid, ad_cnid;
     struct adouble ad;
 
     adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0);
 
-    /* Force checkout every X items */
-    static int cnidcount = 0;
-    cnidcount++;
-    if (cnidcount > 10000) {
-        cnidcount = 0;
-        if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
-            dbd_log(LOGSTD, "Error checkpointing!");
-            return CNID_INVALID;
-        }
-    }
-
     /* Get CNID from ad-file */
-    ad_cnid = 0;
+    ad_cnid = CNID_INVALID;
     if (ADFILE_OK) {
-        ad_init(&ad, myvol);
+        ad_init(&ad, vol);
         if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
             
-            if (dbd_flags & DBD_FLAGS_CLEANUP)
-                return CNID_INVALID;
-
-            if (myvol->v_adouble != AD_VERSION_EA) {
+            if (vol->v_adouble != AD_VERSION_EA) {
                 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
                 return CNID_INVALID;
             }
             dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name);
             adfile_ok = 1;
         } else {
-
-            if (dbd_flags & DBD_FLAGS_FORCE) {
-                ad_cnid = ad_forcegetid(&ad);
-                /* This ensures the changed stamp is written */
-                ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
-                ad_flush(&ad);
-            } else
-                ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
-
-            if (ad_cnid == 0)
+            ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
+            if (ad_cnid == CNID_INVALID)
                 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
             else
                 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
@@ -694,128 +588,26 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
     }
 
     /* Get CNID from database */
-
-    /* Prepare request data */
-    memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
-    memset(&rply, 0, sizeof(struct cnid_dbd_rply));
-    rqst.did = did;
-    rqst.cnid = ad_cnid;
-    if ( ! (myvol->v_flags & AFPVOL_NODEV))
-        rqst.dev = st->st_dev;
-    rqst.ino = st->st_ino;
-    rqst.type = S_ISDIR(st->st_mode)?1:0;
-    rqst.name = (char *)name;
-    rqst.namelen = strlen(name);
-
-    /* Query the database */
-    ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
-    if (dbif_txn_close(dbd, ret) != 0)
+    if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID)
         return CNID_INVALID;
-    if (rply.result == CNID_DBD_RES_OK) {
-        db_cnid = rply.cnid;
-    } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
-        if ( ! (dbd_flags & DBD_FLAGS_FORCE))
-            dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
-        db_cnid = 0;
-    } else {
-        dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
-        db_cnid = 0;
-    }
 
-    /* Compare results from both CNID searches */
-    if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
-        /* Everything is fine */
-        return db_cnid;
-    } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
+    /* Compare CNID from db and adouble file */
+    if (ad_cnid != db_cnid && adfile_ok == 0) {
         /* Mismatch, overwrite ad file with value from db */
-        dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            dbd_log(LOGSTD, "Updating AppleDouble file for '%s/%s' with CNID: %u from database",
-                            cwdbuf, name, ntohl(db_cnid));
-            ad_init(&ad, myvol);
-            if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
-                dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                        cwdbuf, name, strerror(errno));
-                return CNID_INVALID;
-            }
-            ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-            ad_flush(&ad);
-            ad_close(&ad, ADFLAGS_HF);
-        }
-        return db_cnid;
-    } else if (ad_cnid && (db_cnid == 0)) {
-        /* in ad-file but not in db */
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            /* Ensure the cnid from the ad-file is not already occupied by another file */
-            dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
-                    ntohl(ad_cnid));
-
-            rqst.cnid = ad_cnid;
-            ret = dbd_resolve(dbd, &rqst, &rply);
-            if (rply.result == CNID_DBD_RES_OK) {
-                /* Occupied! Choose another, update ad-file */
-                ret = dbd_add(dbd, &rqst, &rply, 1);
-                if (dbif_txn_close(dbd, ret) != 0)
-                    return CNID_INVALID;
-                db_cnid = rply.cnid;
-                dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
-
-                if (ADFILE_OK && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
-                    dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
-                            cwdbuf, name, ntohl(db_cnid));
-                    ad_init(&ad, myvol);
-                    if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
-                        dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                                cwdbuf, name, strerror(errno));
-                        return CNID_INVALID;
-                    }
-                    ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-                    ad_flush(&ad);
-                    ad_close(&ad, ADFLAGS_HF);
-                }
-                return db_cnid;
-            }
-
-            dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
-                    cwdbuf, name, ntohl(ad_cnid));
-            rqst.cnid = ad_cnid;
-            ret = dbd_rebuild_add(dbd, &rqst, &rply);
-            if (dbif_txn_close(dbd, ret) != 0)
-                return CNID_INVALID;
-        }
-        return ad_cnid;
-    } else if ((db_cnid == 0) && (ad_cnid == 0)) {
-        /* No CNID at all, we clearly have to allocate a fresh one... */
-        /* Note: the next test will use this new CNID too! */
-        if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            /* add to db */
-            ret = dbd_add(dbd, &rqst, &rply, 1);
-            if (dbif_txn_close(dbd, ret) != 0)
-                return CNID_INVALID;
-            db_cnid = rply.cnid;
-            dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
-        }
-    }
-
-    if ((ad_cnid == 0) && db_cnid) {
-        /* in db but zeroID in ad-file, write it to ad-file */
-        if (ADFILE_OK && ! (dbd_flags & DBD_FLAGS_SCAN)) {            
-            dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
-                    cwdbuf, name, ntohl(db_cnid));
-            ad_init(&ad, myvol);
-            if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
-                dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
-                        cwdbuf, name, strerror(errno));
-                return CNID_INVALID;
-            }
-            ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
-            ad_flush(&ad);
-            ad_close(&ad, ADFLAGS_HF);
+        dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u",
+                cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
+        ad_init(&ad, vol);
+        if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
+            dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+                    cwdbuf, name, strerror(errno));
+            return CNID_INVALID;
         }
-        return db_cnid;
+        ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+        ad_flush(&ad);
+        ad_close(&ad, ADFLAGS_HF);
     }
 
-    return CNID_INVALID;
+    return db_cnid;
 }
 
 /*
@@ -826,7 +618,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
 */
 static int dbd_readdir(int volroot, cnid_t did)
 {
-    int cwd, ret = 0, adfile_ok, addir_ok, encoding_ok;
+    int cwd, ret = 0, adfile_ok, addir_ok;
     cnid_t cnid = 0;
     const char *name;
     DIR *dp;
@@ -879,7 +671,7 @@ static int dbd_readdir(int volroot, cnid_t did)
         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
             continue;
 
-        if (!myvol->vfs->vfs_validupath(myvol, ep->d_name)) {
+        if (!vol->vfs->vfs_validupath(vol, ep->d_name)) {
             dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name);
             continue;
         }
@@ -893,10 +685,8 @@ static int dbd_readdir(int volroot, cnid_t did)
         switch (st.st_mode & S_IFMT) {
         case S_IFREG:
         case S_IFDIR:
-            break;
         case S_IFLNK:
-            dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
-            continue;
+            break;
         default:
             dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
@@ -933,47 +723,25 @@ static int dbd_readdir(int volroot, cnid_t did)
         if (ADDIR_OK)
             adfile_ok = check_adfile(ep->d_name, &st, &name);
 
-        if (name == NULL) {
-            name = ep->d_name;
-        } else {
-            update_cnid(did, &st, ep->d_name, name);
-        }
+        if (!S_ISLNK(st.st_mode)) {
+            if (name == NULL) {
+                name = ep->d_name;
+            } else {
+                update_cnid(did, &st, ep->d_name, name);
+            }
 
-        if ( ! nocniddb) {
             /* Check CNIDs */
             cnid = check_cnid(name, did, &st, adfile_ok);
 
-            /* Now add this object to our rebuild dbd */
-            if (cnid && dbd_rebuild) {
-                static uint count = 0;
-                rqst.cnid = rply.cnid;
-                ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
-                if (dbif_txn_close(dbd_rebuild, ret) != 0)
-                    return -1;
-                if (rply.result != CNID_DBD_RES_OK) {
-                    dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
-                             cnid, cwdbuf, name);
-                    return -1;
-                }
-                count++;
-                if (count == 10000) {
-                    if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
-                        dbd_log(LOGSTD, "Error checkpointing!");
-                        return -1;
-                    }
-                    count = 0;
-                }
-            }
+            /* Check EA files */
+            if (vol->v_vfs_ea == AFPVOL_EA_AD)
+                check_eafiles(name);
         }
 
-        /* Check EA files */
-        if (myvol->v_vfs_ea == AFPVOL_EA_AD)
-            check_eafiles(name);
-
         /**************************************************************************
           Recursion
         **************************************************************************/
-        if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
+        if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */
             strcat(cwdbuf, "/");
             strcat(cwdbuf, name);
             dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
@@ -1000,7 +768,7 @@ static int dbd_readdir(int volroot, cnid_t did)
     /*
       Use results of previous checks
     */
-    if ((myvol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
+    if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
         if (rmdir(ADv2_DIRNAME) != 0) {
             switch (errno) {
             case ENOENT:
@@ -1015,246 +783,57 @@ static int dbd_readdir(int volroot, cnid_t did)
     return ret;
 }
 
-static int scanvol(struct vol *vol, dbd_flags_t flags)
+/*
+  Main func called from cmd_dbd.c
+*/
+int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags)
 {
+    EC_INIT;
     struct stat st;
 
-    /* Make this stuff accessible from all funcs easily */
-    myvol = vol;
-    dbd_flags = flags;
-
     /* Run with umask 0 */
     umask(0);
 
-    strcpy(cwdbuf, myvol->v_path);
-    chdir(myvol->v_path);
+    /* Make vol accessible for all funcs */
+    vol = vol_in;
+    dbd_flags = flags;
 
-    if ((myvol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
+    /* We only support unicode volumes ! */
+    if (vol->v_volcharset != CH_UTF8) {
+        dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
+        EC_FAIL;
+    }
+
+    /*
+     * Get CNID database stamp, cnid_getstamp() passes the buffer,
+     * then cnid_resolve() actually gets the value from the db
+     */
+    cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp));
+
+    if (setjmp(jmp) != 0) {
+        EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */
+    }
+
+    strcpy(cwdbuf, vol->v_path);
+    chdir(vol->v_path);
+
+    if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
         if (lstat(".", &st) != 0)
-            return -1;
+            EC_FAIL;
         if (ad_convert(".", &st, vol, NULL) != 0) {
             switch (errno) {
             case ENOENT:
                 break;
             default:
-                dbd_log(LOGSTD, "Conversion error for \"%s\": %s", myvol->v_path, strerror(errno));
+                dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno));
                 break;
             }
         }
     }
 
     /* Start recursion */
-    if (dbd_readdir(1, htonl(2)) < 0)  /* 2 = volumeroot CNID */
-        return -1;
-
-    return 0;
-}
-
-/*
-  Remove all CNIDs from dbd that are not in dbd_rebuild
-*/
-static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
-{
-    int ret = 0, deleted = 0;
-    cnid_t dbd_cnid = 0, rebuild_cnid = 0;
-    struct cnid_dbd_rqst rqst;
-    struct cnid_dbd_rply rply;
-
-    /* jump over rootinfo key */
-    if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
-        return;
-    if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
-        return;
-
-    /* Get first id from dbd_rebuild */
-    if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
-        return;
-
-    /* Start main loop through dbd: get CNID from dbd */
-    while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
-        /* Check if we got a termination signal */
-        if (alarmed)
-            longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
-
-        if (deleted > 1000) {
-            deleted = 0;
-            if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
-                dbd_log(LOGSTD, "Error checkpointing!");
-                goto cleanup;
-            }
-        }
-
-        /* This should be the normal case: CNID is in both dbs */
-        if (dbd_cnid == rebuild_cnid) {
-            /* Get next CNID from rebuild db */
-            if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
-                /* Some error */
-                goto cleanup;
-            } else if (ret == 0) {
-                /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
-                while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
-                    dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
-                    if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-                        rqst.cnid = htonl(dbd_cnid);
-                        if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
-                            dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
-                            (void)dbif_txn_abort(dbd);
-                            goto cleanup;
-                        }
-                        
-                        if (dbif_txn_close(dbd, ret) != 0)
-                            return;
-                        deleted++;
-                    }
-                    /* Check if we got a termination signal */
-                    if (alarmed)
-                        longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
-                }
-                return;
-            } else
-                /* Normal case (ret=1): continue while loop */
-                continue;
-        }
-
-        if (dbd_cnid < rebuild_cnid) {
-            /* CNID is orphaned -> delete */
-            dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
-            if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-                rqst.cnid = htonl(dbd_cnid);
-                if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
-                    dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
-                    (void)dbif_txn_abort(dbd);
-                    goto cleanup;
-                }
-                if (dbif_txn_close(dbd, ret) != 0)
-                    return;
-                deleted++;
-            }
-            continue;
-        }
-
-        if (dbd_cnid > rebuild_cnid) {
-            dbif_idwalk(dbd, NULL, 1); /* Close cursor */
-            dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
-            (void)dbif_txn_close(dbd, 2);
-            (void)dbif_txn_close(dbd_rebuild, 2);                
-            dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
-            dbif_dump(dbd_rebuild, 0);
-            dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
-            goto cleanup;
-        }
-    } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
-
-cleanup:
-    dbif_idwalk(dbd, NULL, 1); /* Close cursor */
-    dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
-    return;
-}
-
-static const char *get_tmpdb_path(void)
-{
-    pid_t pid = getpid();
-    static char path[MAXPATHLEN];
-    snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
-    if (mkdir(path, 0755) != 0)
-        return NULL;
-    return path;
-}
-
-/*
-  Main func called from cmd_dbd.c
-*/
-int cmd_dbd_scanvol(DBD *dbd_ref, struct vol *vol, dbd_flags_t flags)
-{
-    int ret = 0;
-    struct db_param db_param = { 0 };
-    const char *tmpdb_path = NULL;
-
-    /* Set cachesize for in-memory rebuild db */
-    db_param.cachesize = 64 * 1024;         /* 64 MB */
-    db_param.maxlocks = DEFAULT_MAXLOCKS;
-    db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
-    db_param.logfile_autoremove = 1;
-
-    /* Make it accessible for all funcs */
-    dbd = dbd_ref;
-
-    /* We only support unicode volumes ! */
-    if (vol->v_volcharset != CH_UTF8) {
-        dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
-        return -1;
-    }
-
-    /* Get volume stamp */
-    dbd_getstamp(dbd, &rqst, &rply);
-    if (rply.result != CNID_DBD_RES_OK) {
-        ret = -1;
-        goto exit;
-    }
-    memcpy(stamp, rply.name, CNID_DEV_LEN);
-
-    /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
-    if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) {
-        tmpdb_path = get_tmpdb_path();
-        if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
-            ret = -1;
-            goto exit;
-        }
-
-        if (dbif_env_open(dbd_rebuild,
-                          &db_param,
-                          DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
-            dbd_log(LOGSTD, "error opening tmp database!");
-            goto exit;
-        }
+    EC_NEG1( dbd_readdir(1, htonl(2)) );  /* 2 = volumeroot CNID */
 
-        if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
-            ret = -1;
-            goto exit;
-        }
-
-        if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
-            ret = -1;
-            goto exit;
-        }
-    }
-
-    if (setjmp(jmp) != 0) {
-        ret = 0;                /* Got signal, jump from dbd_readdir */
-        goto exit;
-    }
-
-    /* scanvol */
-    if ((scanvol(vol, flags)) != 0) {
-        ret = -1;
-        goto exit;
-    }
-
-exit:
-    if (! nocniddb) {
-        if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0)
-            ret = -1;
-        if (dbd_rebuild)
-            if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0)
-                ret = -1;
-        if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
-            /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
-               other clients in between our pass 1 and 2 */
-            delete_orphaned_cnids(dbd, dbd_rebuild, flags);
-    }
-
-    if (dbd_rebuild) {
-        dbd_log(LOGDEBUG, "Closing tmp db");
-        dbif_close(dbd_rebuild);
-
-        if (tmpdb_path) {
-            char cmd[8 + MAXPATHLEN];
-            snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
-            dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
-            system(cmd);
-            snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);
-            system(cmd);
-        }        
-    }
-    return ret;
+EC_CLEANUP:
+    EC_EXIT;
 }
index dcf7d5faaa78b89fb68f866a979d3f5c55d7b4af..7b7664d52d6febb546718b1eaa42b710c2da5c76 100644 (file)
@@ -92,6 +92,7 @@
 #include <atalk/compat.h>
 #include <atalk/errchk.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/volume.h>
 
@@ -183,8 +184,8 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
     time(&t);
     if (!up) {
         /* find an empty slot (i < maxvol) or the first free slot (i == maxvol)*/
-        for (i = 0; i <= maxvol; i++) {
-            if (srv[i].v_path == NULL && i < MAXVOLS) {
+        for (i = 0; i <= maxvol && i < MAXVOLS; i++) {
+            if (srv[i].v_path == NULL) {
                 up = &srv[i];
                 if ((up->v_path = strdup(volpath)) == NULL)
                     return -1;
@@ -286,7 +287,6 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
 static int set_dbdir(const char *dbdir, const char *vpath)
 {
     EC_INIT;
-    int status;
     struct stat st;
     bstring oldpath, newpath;
     char *cmd_argv[4];
@@ -301,7 +301,7 @@ static int set_dbdir(const char *dbdir, const char *vpath)
         EC_FAIL;
     }
 
-    if (lstat(bdata(oldpath), &st) == 0 && lstat(bdata(newpath), &st) != 0 && errno == ENOENT) {
+    if (lstat(cfrombstr(oldpath), &st) == 0 && lstat(cfrombstr(newpath), &st) != 0 && errno == ENOENT) {
         /* There's an .AppleDB in the volume root, we move it */
         cmd_argv[0] = "mv";
         cmd_argv[1] = bdata(oldpath);
@@ -315,7 +315,7 @@ static int set_dbdir(const char *dbdir, const char *vpath)
 
     }
 
-    if (lstat(bdata(newpath), &st) < 0 && mkdir(bdata(newpath), 0755 ) < 0) {
+    if (lstat(cfrombstr(newpath), &st) < 0 && mkdir(cfrombstr(newpath), 0755 ) < 0) {
         LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", bdata(newpath));
         EC_FAIL;
     }
@@ -326,44 +326,6 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
-/* ------------------ */
-static uid_t user_to_uid (char *username)
-{
-    struct passwd *this_passwd;
-
-    /* check for anything */
-    if ( !username || strlen ( username ) < 1 ) return 0;
-
-    /* grab the /etc/passwd record relating to username */
-    this_passwd = getpwnam ( username );
-
-    /* return false if there is no structure returned */
-    if (this_passwd == NULL) return 0;
-
-    /* return proper uid */
-    return this_passwd->pw_uid;
-
-}
-
-/* ------------------ */
-static gid_t group_to_gid ( char *group)
-{
-    struct group *this_group;
-
-    /* check for anything */
-    if ( !group || strlen ( group ) < 1 ) return 0;
-
-    /* grab the /etc/groups record relating to group */
-    this_group = getgrnam ( group );
-
-    /* return false if there is no structure returned */
-    if (this_group == NULL) return 0;
-
-    /* return proper gid */
-    return this_group->gr_gid;
-
-}
-
 /* ------------------ */
 static void catch_child(int sig _U_) 
 {
@@ -471,7 +433,6 @@ int main(int argc, char *argv[])
     int    cc;
     uid_t  uid = 0;
     gid_t  gid = 0;
-    int    err = 0;
     int    debug = 0;
     int    ret;
     sigset_t set;
@@ -502,13 +463,13 @@ int main(int argc, char *argv[])
     if (afp_config_parse(&obj, "cnid_metad") != 0)
         daemon_exit(1);
 
-    if (load_volumes(&obj, NULL) != 0)
+    if (load_volumes(&obj) != 0)
         daemon_exit(1);
 
     (void)setlimits();
 
     host = iniparser_getstrdup(obj.iniconfig, INISEC_GLOBAL, "cnid listen", "localhost:4700");
-    if (port = strrchr(host, ':'))
+    if ((port = strrchr(host, ':')))
         *port++ = 0;
     else
         port = DEFAULTPORT;
@@ -604,7 +565,7 @@ int main(int argc, char *argv[])
 
         LOG(log_debug, logtype_cnid, "main: request for volume: %s", volpath);
 
-        if (load_volumes(&obj, NULL) != 0) {
+        if (load_volumes(&obj) != 0) {
             LOG(log_severe, logtype_cnid, "main: error reloading config");
             goto loop_end;
         }
index c89f8e5cec00890c2d9b4a3e8410241cc7b684a7..3c8c102fc17cd39a16f9d09c89068b476833d9bd 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: db_param.c,v 1.9 2009-11-23 19:04:14 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (c) Frank Lahm 2009
index bfa60954037c19bbfca7d998ab34d9725f50792a..7b8da1cadab6fceb87952aa06613ec8848e74437 100644 (file)
@@ -14,8 +14,8 @@
 extern int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
 extern int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply);
 
-extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int nolookup);
-extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int roflag);
+extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
+extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_get(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_resolve(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_update(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
index 36c0856867b5e4c703926f34c4ac6de405ba3d94..db9c6cc29d23f5c3ba023032e97cd7e325b8ea54 100644 (file)
@@ -74,7 +74,7 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
 {
     static cnid_t id;
     static char buf[ROOTINFO_DATALEN];
-    DBT rootinfo_key, rootinfo_data;
+    DBT rootinfo_key, rootinfo_data, key, data;
     int rc;
     cnid_t hint;
 
@@ -83,47 +83,63 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
     rootinfo_key.data = ROOTINFO_KEY;
     rootinfo_key.size = ROOTINFO_KEYLEN;
 
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+
     if (id == 0) {
-        if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) < 0) {
+        if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) != 1) {
             rply->result = CNID_DBD_RES_ERR_DB;
             return -1;
         }
-        if (rc == 0) {
-            /* no rootinfo key yet */
-            memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+        memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
+        memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
+        id = ntohl(hint);
+        if (id < CNID_START - 1)
             id = CNID_START - 1;
-        } else {
-            memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
-            memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
-            id = ntohl(hint);
-            if (id < CNID_START - 1)
-                id = CNID_START - 1;
-        }
     }
 
-    /* If we've hit the max CNID allowed, we return an error. CNID
-     * needs to be recycled before proceding. */
-    if (++id == CNID_INVALID) {
-        rply->result = CNID_DBD_RES_ERR_MAX;
-        return -1;
+    cnid_t trycnid, tmp;
+
+    while (true) {
+        if (rply->cnid != CNID_INVALID) {
+            trycnid = ntohl(rply->cnid);
+            rply->cnid = CNID_INVALID;
+        } else {
+            if (++id == CNID_INVALID)
+                id = CNID_START;
+            trycnid = id;
+        }
+        tmp = htonl(trycnid);
+        key.data = &tmp;
+        key.size = sizeof(cnid_t);
+        rc = dbif_get(dbd, DBIF_CNID, &key, &data, 0);
+        if (rc == 0) {
+            break;
+        } else if (rc == -1) {
+            rply->result = CNID_DBD_RES_ERR_DB;
+            return -1;
+        }
     }
 
-    rootinfo_data.data = buf;
-    rootinfo_data.size = ROOTINFO_DATALEN;
-    hint = htonl(id);
-    memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
+    if (trycnid == id) {
+        rootinfo_data.data = buf;
+        rootinfo_data.size = ROOTINFO_DATALEN;
+        hint = htonl(id);
+        memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
 
-    if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
-        rply->result = CNID_DBD_RES_ERR_DB;
-        return -1;
+        if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
+            rply->result = CNID_DBD_RES_ERR_DB;
+            return -1;
+        }
     }
-    rply->cnid = hint;
+
+    rply->cnid = htonl(trycnid);
     return 0;
 }
 
 /* ------------------------ */
 /* We need a nolookup version for `dbd` */
-int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int nolookup)
+int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     rply->namelen = 0;
 
@@ -131,24 +147,25 @@ int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, in
         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
     /* See if we have an entry already and return it if yes */
-    if (! nolookup) {
-        if (dbd_lookup(dbd, rqst, rply, 0) < 0) {
-            LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): error in dbd_lookup",
-                ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-            return -1;
-        }
+    if (dbd_lookup(dbd, rqst, rply) < 0) {
+        LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): error in dbd_lookup",
+            ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+        return -1;
+    }
 
-        if (rply->result == CNID_DBD_RES_OK) {
-            /* Found it. rply->cnid is the correct CNID now. */
-            LOG(log_debug, logtype_cnid, "dbd_add: dbd_lookup success --> CNID: %u", ntohl(rply->cnid));
-            return 1;
-        }
+    if (rply->result == CNID_DBD_RES_OK) {
+        /* Found it. rply->cnid is the correct CNID now. */
+        LOG(log_debug, logtype_cnid, "dbd_add: dbd_lookup success --> CNID: %u", ntohl(rply->cnid));
+        return 1;
     }
 
     LOG(log_debug, logtype_cnid, "dbd_add(did:%u, '%s', dev/ino:0x%llx/0x%llx): {adding to database ...}",
         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-
+    if (rqst->cnid) {
+        /* rqst->cnid is the cnid "hint"/backup from the adouble file */
+        rply->cnid = rqst->cnid;
+    }
     if (get_cnid(dbd, rply) < 0) {
         if (rply->result == CNID_DBD_RES_ERR_MAX) {
             LOG(log_error, logtype_cnid, "dbd_add: FATAL: CNID database has reached its limit.");
index e621704f9339305aeac21c9e1e35555c65bdcb30..98ce2210644a1c9df415789f8d82d334cd6ed3d1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_dbcheck.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index a4a3ca63e29a8c58aad3b15a1ace3e66a2793701..f34a9d500621ac9d967aab7200f111bc5d512bae 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_delete.c,v 1.5 2009-07-12 09:21:34 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index 0ef71b19670c8c734ec34d3f825604559a41e682..c1c7bb8cc44f28e100f1540f2ff110f29ec7f872 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_get.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index 197a6db12087eb2f35e052ba5b0c29f30e34d398..ffba39721b620589d82b318baa3156070a7deeef 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: dbd_getstamp.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index 6169738b5f66e3230bb55fbb246eb38b08259d0f..f77b9fc2cb5f95a3abea20009fee1d980a7296ed 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_lookup.c,v 1.18 2010-01-19 14:57:11 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (C) Frank Lahm 2009
@@ -120,6 +119,7 @@ to be safe we must assign new CNIDs to both files.
 
 #include <atalk/logger.h>
 #include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid.h>
 
 #include "pack.h"
 #include "dbif.h"
@@ -130,7 +130,7 @@ to be safe we must assign new CNIDs to both files.
  *  up the database if there's a problem.
  */
 
-int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
+int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     unsigned char *buf;
     DBT key, devdata, diddata;
@@ -211,16 +211,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 
                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-            if (! roflag) {
-                rqst->cnid = id_devino;
-                rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
-                if (rc < 0) {
-                    LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
-                        rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-                    return -1;
-                }
+            rqst->cnid = id_devino;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+                return -1;
             }
         }
 
@@ -230,16 +228,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 
                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
 
-            if (! roflag) {
-                rqst->cnid = id_didname;
-                rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
-                rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
-                if (rc < 0) {
-                    LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
-                        rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-                    return -1;
-                }
+            rqst->cnid = id_didname;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+                return -1;
             }
         }
 
@@ -262,15 +258,14 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             ntohl(rqst->did), rqst->name, ntohl(id_didname),
             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
 
-        if (! roflag) {
-            rqst->cnid = id_devino;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
+        rqst->cnid = id_devino;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
+
+        rqst->cnid = id_didname;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
 
-            rqst->cnid = id_didname;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
-        }
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
@@ -282,12 +277,11 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
             update = 1;
         } else {
-            if ( ! roflag) {
-                rqst->cnid = id_devino;
-                if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                    return -1;
-            }
+            rqst->cnid = id_devino;
+            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+                return -1;
             rply->result = CNID_DBD_RES_NOTFOUND;
+            rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
             return 1;
         }
     }
@@ -295,17 +289,16 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
     if ( ! devino) {
         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-        if ( ! roflag) {
-            rqst->cnid = id_didname;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
-        }
+        rqst->cnid = id_didname;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
         rply->result = CNID_DBD_RES_NOTFOUND;
+        rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
         return 1;
     }
 
     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
-    if (!update || roflag) {
+    if (!update) {
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
index 562838b384a28dacc46873f8742a8296a6a2eba1..f1bf7aca30fb219b4a9cb68d63c41c163d0e2a19 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_rebuild_add.c,v 1.4 2009-12-23 10:18:48 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2005
  * All Rights Reserved.  See COPYING.
index bb3561caba7a1018605be36649d85b13abb8ab29..53407757969b008256b7de3344250c53814f3d21 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_resolve.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index b21f259b860f40e12c71167d27c4695a9bc1bda9..3dae61cdad2f6ba5561f14708d4c37f39dc5f27c 100644 (file)
@@ -22,6 +22,7 @@
 #include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/errchk.h>
+#include <atalk/cnid.h>
 
 #include "db_param.h"
 #include "dbif.h"
@@ -88,13 +89,11 @@ EC_CLEANUP:
 static int dbif_init_rootinfo(DBD *dbd, int version)
 {
     DBT key, data;
-    uint32_t v;
+    uint32_t uint32;
     char buf[ROOTINFO_DATALEN];
 
     LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
 
-    v = version;
-    v = htonl(v);
 
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
@@ -104,7 +103,13 @@ static int dbif_init_rootinfo(DBD *dbd, int version)
     data.size = ROOTINFO_DATALEN;
 
     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
-    memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
+
+    uint32 = htonl(CNID_START - 1);
+    memcpy(buf + CNID_TYPE_OFS, &uint32, sizeof(uint32));
+
+    uint32 = htonl(version);
+    memcpy(buf + CNID_DID_OFS, &uint32, sizeof(uint32));
+
     if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
         return -1;
 
@@ -326,97 +331,6 @@ exit:
     return ret;
 }
 
-/*!
- * Get lock on db lock file
- *
- * @args cmd       (r) lock command:
- *                     LOCK_FREE:   close lockfd
- *                     LOCK_UNLOCK: unlock lockm keep lockfd open
- *                     LOCK_EXCL:   F_WRLCK on lockfd
- *                     LOCK_SHRD:   F_RDLCK on lockfd
- * @args dbpath    (r) path to lockfile, only used on first call,
- *                     later the stored fd is used
- * @returns            LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
- *                     LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
- *                     success, 0 if the lock couldn't be acquired, -1 on other errors
- */
-int get_lock(int cmd, const char *dbpath)
-{
-    static int lockfd = -1;
-    int ret;
-    char lockpath[PATH_MAX];
-    struct stat st;
-
-    LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
-        cmd == LOCK_EXCL ? "LOCK_EXCL" :
-        cmd == LOCK_SHRD ? "LOCK_SHRD" :
-        cmd == LOCK_FREE ? "LOCK_FREE" :
-        cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN" , dbpath ? dbpath : "");
-
-    switch (cmd) {
-    case LOCK_FREE:
-        if (lockfd == -1)
-            return -1;
-        close(lockfd);
-        lockfd = -1;
-        return 0;
-
-    case LOCK_UNLOCK:
-        if (lockfd == -1)
-            return -1;
-        return unlock(lockfd, 0, SEEK_SET, 0);
-
-    case LOCK_EXCL:
-    case LOCK_SHRD:
-        if (lockfd == -1) {
-            if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
-                LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
-                return -1;
-            }
-            strncpy(lockpath, dbpath, PATH_MAX - 1);
-            strcat(lockpath, "/");
-            strcat(lockpath, LOCKFILENAME);
-
-            if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
-                LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
-                return -1;
-            }
-
-            if ((stat(dbpath, &st)) != 0) {
-                LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
-                return -1;
-            }
-
-            if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
-                LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
-                         strerror(errno));
-                return -1;
-            }
-        }
-    
-        if (cmd == LOCK_EXCL)
-            ret = write_lock(lockfd, 0, SEEK_SET, 0);
-        else
-            ret = read_lock(lockfd, 0, SEEK_SET, 0);
-
-        if (ret != 0) {
-            if (cmd == LOCK_SHRD)
-                LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
-            return 0; 
-        }
-
-        LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
-            cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");    
-        return cmd;
-
-    default:
-        return -1;
-    } /* switch(cmd) */
-
-    /* deadc0de, never get here */
-    return -1;
-}
-
 /* --------------- */
 DBD *dbif_init(const char *envhome, const char *filename)
 {
@@ -813,11 +727,6 @@ int dbif_env_remove(const char *path)
 
     LOG(log_debug, logtype_cnid, "Trying to remove BerkeleyDB environment");
 
-    if (get_lock(LOCK_EXCL, path) != LOCK_EXCL) {
-        LOG(log_debug, logtype_cnid, "CNID db \"%s\" in use, not removing BerkeleyDB environment", path);
-        return 0;
-    }
-    
     if (NULL == (dbd = dbif_init(path, "cnid2.db")))
         return -1;
 
index 8c71de36b96221f598f64f4943b0878e7906f444..a6c88a98068ad30f9535e03d6cb694c12e17e7dd 100644 (file)
@@ -64,7 +64,6 @@
 #define DBIF_IDX_DIDNAME   2
 #define DBIF_IDX_NAME      3
 
-/* get_lock cmd and return value */
 #define LOCKFILENAME  "lock"
 #define LOCK_FREE          0
 #define LOCK_UNLOCK        1
@@ -91,9 +90,6 @@ typedef struct {
     db_table db_table[DBIF_DB_CNT];
 } DBD;
 
-/* Functions */
-extern int get_lock(int cmd, const char *dbpath);
-
 extern DBD *dbif_init(const char *envhome, const char *dbname);
 extern int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags);
 extern int dbif_open(DBD *dbd, struct db_param *dbp, int reindex);
index 8ed0e5ff2bcd37796511c3094689ce3b7dfb6da8..0067504a0c6b3c4fd081e7eccbf1550272498a2b 100644 (file)
@@ -26,7 +26,9 @@
 #include <atalk/logger.h>
 #include <atalk/errchk.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/util.h>
 
 #include "db_param.h"
 #include "dbif.h"
@@ -43,6 +45,9 @@
 static DBD *dbd;
 static int exit_sig = 0;
 static int db_locked;
+static bstring dbpath;
+static struct db_param *dbp;
+static struct vol *vol;
 
 static void sig_exit(int signo)
 {
@@ -77,9 +82,205 @@ static void block_sigs_onoff(int block)
   of the cnid_dbd_rply structure contains further details.
 
 */
-#ifndef min
-#define min(a,b)        ((a)<(b)?(a):(b))
-#endif
+
+/*!
+ * Get lock on db lock file
+ *
+ * @args cmd       (r) lock command:
+ *                     LOCK_FREE:   close lockfd
+ *                     LOCK_UNLOCK: unlock lockm keep lockfd open
+ *                     LOCK_EXCL:   F_WRLCK on lockfd
+ *                     LOCK_SHRD:   F_RDLCK on lockfd
+ * @args dbpath    (r) path to lockfile, only used on first call,
+ *                     later the stored fd is used
+ * @returns            LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
+ *                     LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
+ *                     success, 0 if the lock couldn't be acquired, -1 on other errors
+ */
+static int get_lock(int cmd, const char *dbpath)
+{
+    static int lockfd = -1;
+    int ret;
+    char lockpath[PATH_MAX];
+    struct stat st;
+
+    LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
+        cmd == LOCK_EXCL ? "LOCK_EXCL" :
+        cmd == LOCK_SHRD ? "LOCK_SHRD" :
+        cmd == LOCK_FREE ? "LOCK_FREE" :
+        cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN",
+        dbpath ? dbpath : "");
+
+    switch (cmd) {
+    case LOCK_FREE:
+        if (lockfd == -1)
+            return -1;
+        close(lockfd);
+        lockfd = -1;
+        return 0;
+
+    case LOCK_UNLOCK:
+        if (lockfd == -1)
+            return -1;
+        return unlock(lockfd, 0, SEEK_SET, 0);
+
+    case LOCK_EXCL:
+    case LOCK_SHRD:
+        if (lockfd == -1) {
+            if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
+                LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
+                return -1;
+            }
+            strncpy(lockpath, dbpath, PATH_MAX - 1);
+            strcat(lockpath, "/");
+            strcat(lockpath, LOCKFILENAME);
+
+            if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
+                LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
+                return -1;
+            }
+
+            if ((stat(dbpath, &st)) != 0) {
+                LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
+                return -1;
+            }
+
+            if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
+                LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
+                         strerror(errno));
+                return -1;
+            }
+        }
+    
+        if (cmd == LOCK_EXCL)
+            ret = write_lock(lockfd, 0, SEEK_SET, 0);
+        else
+            ret = read_lock(lockfd, 0, SEEK_SET, 0);
+
+        if (ret != 0) {
+            if (cmd == LOCK_SHRD)
+                LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
+            return 0; 
+        }
+
+        LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
+            cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");    
+        return cmd;
+
+    default:
+        return -1;
+    } /* switch(cmd) */
+
+    /* deadc0de, never get here */
+    return -1;
+}
+
+static int open_db(void)
+{
+    EC_INIT;
+
+    /* Get db lock */
+    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        EC_FAIL;
+    }
+
+    if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
+        EC_FAIL;
+
+    /* Only recover if we got the lock */
+    if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
+
+    if (dbif_open(dbd, dbp, 0) < 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
+
+EC_CLEANUP:
+    if (ret != 0) {
+        if (dbd) {
+            (void)dbif_close(dbd);
+            dbd = NULL;
+        }
+    }
+
+    EC_EXIT;
+}
+
+static int delete_db(void)
+{
+    EC_INIT;
+    int cwd = -1;
+
+    EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) );
+    EC_NEG1( cwd = open(".", O_RDONLY) );
+    chdir(cfrombstr(dbpath));
+    system("rm -f cnid2.db lock log.* __db.*");
+
+    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        EC_FAIL;
+    }
+
+    LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname);
+
+EC_CLEANUP:
+    if (cwd != -1) {
+        fchdir(cwd);
+        close(cwd);
+    }
+    EC_EXIT;
+}
+
+
+/**
+ * Close dbd if open, delete it, reopen
+ *
+ * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
+ * and last used CNID
+ **/
+static int reinit_db(void)
+{
+    EC_INIT;
+    DBT key, data;
+    bool copyRootInfo = false;
+
+    if (dbd) {
+        memset(&key, 0, sizeof(key));
+        memset(&data, 0, sizeof(data));
+
+        key.data = ROOTINFO_KEY;
+        key.size = ROOTINFO_KEYLEN;
+
+        if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
+            LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
+            copyRootInfo = false;
+        } else {
+            copyRootInfo = true;
+        }
+        (void)dbif_close(dbd);
+    }
+
+    EC_ZERO_LOG( delete_db() );
+    EC_ZERO_LOG( open_db() );
+
+    if (copyRootInfo == true) {
+        memset(&key, 0, sizeof(key));
+        key.data = ROOTINFO_KEY;
+        key.size = ROOTINFO_KEYLEN;
+
+        if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
+            LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
+            EC_FAIL;
+        }
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
 
 static int loop(struct db_param *dbp)
 {
@@ -110,7 +311,7 @@ static int loop(struct db_param *dbp)
         dbp->flush_interval, timebuf);
 
     while (1) {
-        timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
+        timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout);
         if (timeout > now)
             timeout -= now;
         else
@@ -145,7 +346,7 @@ static int loop(struct db_param *dbp)
                 ret = 1;
                 break;
             case CNID_DBD_OP_ADD:
-                ret = dbd_add(dbd, &rqst, &rply, 0);
+                ret = dbd_add(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_GET:
                 ret = dbd_get(dbd, &rqst, &rply);
@@ -154,7 +355,7 @@ static int loop(struct db_param *dbp)
                 ret = dbd_resolve(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_LOOKUP:
-                ret = dbd_lookup(dbd, &rqst, &rply, 0);
+                ret = dbd_lookup(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_UPDATE:
                 ret = dbd_update(dbd, &rqst, &rply);
@@ -171,6 +372,9 @@ static int loop(struct db_param *dbp)
             case CNID_DBD_OP_SEARCH:
                 ret = dbd_search(dbd, &rqst, &rply);
                 break;
+            case CNID_DBD_OP_WIPE:
+                ret = reinit_db();
+                break;
             default:
                 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
                 ret = -1;
@@ -274,18 +478,15 @@ static void set_signal(void)
 int main(int argc, char *argv[])
 {
     EC_INIT;
-    struct db_param *dbp;
     int delete_bdb = 0;
     int ctrlfd = -1, clntfd = -1;
-    char *logconfig;
     AFPObj obj = { 0 };
-    struct vol *vol;
     char *volpath = NULL;
-    bstring dbpath;
 
     while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
         switch (ret) {
         case 'd':
+            /* this is now just ignored, as we do it automatically anyway */
             delete_bdb = 1;
             break;
         case 'F':
@@ -314,7 +515,7 @@ int main(int argc, char *argv[])
 
     EC_ZERO( afp_config_parse(&obj, "cnid_dbd") );
 
-    EC_ZERO( load_volumes(&obj, NULL) );
+    EC_ZERO( load_volumes(&obj) );
     EC_NULL( vol = getvolbypath(&obj, volpath) );
     EC_ZERO( load_charset(vol) );
     pack_setvol(vol);
@@ -326,29 +527,6 @@ int main(int argc, char *argv[])
 
     switch_to_user(bdata(dbpath));
 
-    /* Get db lock */
-    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) == -1) {
-        LOG(log_error, logtype_cnid, "main: fatal db lock error");
-        EC_FAIL;
-    }
-    if (db_locked != LOCK_EXCL) {
-        /* Couldn't get exclusive lock, try shared lock  */
-        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
-            LOG(log_error, logtype_cnid, "main: fatal db lock error");
-            EC_FAIL;
-        }
-    }
-
-    if (delete_bdb && (db_locked == LOCK_EXCL)) {
-        LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
-        chdir(bdata(dbpath));
-        system("rm -f cnid2.db lock log.* __db.*");
-        if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
-            LOG(log_error, logtype_cnid, "main: fatal db lock error");
-            EC_FAIL;
-        }
-    }
-
     set_signal();
 
     /* SIGINT and SIGTERM are always off, unless we are in pselect */
@@ -358,37 +536,11 @@ int main(int argc, char *argv[])
         EC_FAIL;
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
-        EC_FAIL;
-
-    /* Only recover if we got the lock */
-    if (dbif_env_open(dbd,
-                      dbp,
-                      (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
-        EC_FAIL;
-    LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
-
-    if (dbif_open(dbd, dbp, 0) < 0) {
-        ret = -1;
-        goto close_db;
+    if (open_db() != 0) {
+        LOG(log_error, logtype_cnid, "Failed to open CNID database for volume \"%s\"", vol->v_localname);
+        EC_ZERO_LOG( reinit_db() );
     }
 
-    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
-
-    /* Downgrade db lock  */
-    if (db_locked == LOCK_EXCL) {
-        if (get_lock(LOCK_UNLOCK, NULL) != 0) {
-            ret = -1;
-            goto close_db;
-        }
-
-        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
-            ret = -1;
-            goto close_db;
-        }
-    }
-
-
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
         ret = -1;
         goto close_db;
index ee4d4327a77100bc247b7b28f7848266542c3338..27bde63c31b6a6d665d87f264c85de059b53a96d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: usockfd.h,v 1.5 2009-11-05 14:38:07 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
index b35d264c81819787dbd2197d7f5de21bb640926a..b3ab20a159a232c82f1ce739b6e89947b1b3484f 100644 (file)
@@ -1,8 +1,16 @@
-/*
- * Copyright (c) 1990,1993 Regents of The University of Michigan.
- * All Rights Reserved.  See COPYRIGHT.
- */
-
+/* 
+   Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
@@ -44,7 +52,6 @@ static void kill_childs(int sig, ...);
 
 /* static variables */
 static AFPObj obj;
-static sig_atomic_t got_chldsig;
 static pid_t afpd_pid = -1,  cnid_metad_pid = -1;
 static uint afpd_restarts, cnid_metad_restarts;
 static struct event_base *base;
@@ -116,10 +123,17 @@ static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
     kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, NULL);
 }
 
+/* SIGQUIT callback */
+static void sighup_cb(evutil_socket_t fd, short what, void *arg)
+{
+    LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
+    kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, NULL);
+}
+
 /* SIGCHLD callback */
 static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
 {
-    int status, i;
+    int status;
     pid_t pid;
 
     LOG(log_debug, logtype_afpd, "Got SIGCHLD event");
@@ -139,7 +153,7 @@ static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
 
         if (pid == afpd_pid)
             afpd_pid = -1;
-        else if (pid = cnid_metad_pid)
+        else if (pid == cnid_metad_pid)
             cnid_metad_pid = -1;
         else
             LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
@@ -152,8 +166,6 @@ static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
 /* timer callback */
 static void timer_cb(evutil_socket_t fd, short what, void *arg)
 {
-    static int i = 0;
-
     if (in_shutdown)
         return;
 
@@ -197,7 +209,7 @@ static void kill_childs(int sig, ...)
 /* this get called when error conditions are met that require us to exit gracefully */
 static void netatalk_exit(int ret)
 {
-    server_unlock(_PATH_NETATALK_LOCK);
+    server_unlock(PATH_NETATALK_LOCK);
     exit(ret);
 }
 
@@ -237,7 +249,6 @@ static void usage(void)
 
 int main(int argc, char **argv)
 {
-    const char *configfile = NULL;
     int c, ret, debug = 0;
     sigset_t blocksigs;
     struct timeval tv;
@@ -259,13 +270,13 @@ int main(int argc, char **argv)
         }
     }
 
-    if (check_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+    if (check_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
         exit(EXITERR_SYS);
 
     if (!debug && daemonize(0, 0) != 0)
         exit(EXITERR_SYS);
 
-    if (create_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+    if (create_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
         exit(EXITERR_SYS);
 
     sigfillset(&blocksigs);
@@ -280,7 +291,7 @@ int main(int argc, char **argv)
     LOG(log_note, logtype_default, "Netatalk AFP server starting");
 
     if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
-        LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
+        LOG(log_error, logtype_afpd, "Error starting 'afpd'");
         netatalk_exit(EXITERR_CONF);
     }
 
@@ -296,6 +307,7 @@ int main(int argc, char **argv)
 
     sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
     sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL | EV_PERSIST, sigquit_cb, NULL);
+    sigquit_ev = event_new(base, SIGHUP,  EV_SIGNAL | EV_PERSIST, sighup_cb, NULL);
     sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
     timer_ev = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
 
@@ -311,6 +323,7 @@ int main(int argc, char **argv)
     sigdelset(&blocksigs, SIGTERM);
     sigdelset(&blocksigs, SIGQUIT);
     sigdelset(&blocksigs, SIGCHLD);
+    sigdelset(&blocksigs, SIGHUP);
     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
 
     /* run the event loop */
index d033d96b12c76e322a8fd143b90e283ff8847738..76e9143f6a97bd20e6cab84f8027cff6e44a64ce 100644 (file)
@@ -136,7 +136,11 @@ error:
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) {
     int count = 0;
@@ -927,9 +931,6 @@ static int uam_setup(const char *path)
     if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
         return -1;
 
-    p = gcry_mpi_new(0);
-    g = gcry_mpi_new(0);
-
     LOG(log_debug, logtype_uams, "DHX2: generating mersenne primes");
     /* Generate p and g for DH */
     if (dh_params_generate(PRIMEBITS) != 0) {
@@ -945,6 +946,8 @@ static void uam_cleanup(void)
     uam_unregister(UAM_SERVER_LOGIN, "DHX2");
     uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
 
+    LOG(log_debug, logtype_uams, "DHX2: uam_cleanup");
+
     gcry_mpi_release(p);
     gcry_mpi_release(g);
 }
index c4a322ad48220ca899783ce5d0a83c17b4a4650c..a2737d0f920f90b746df9efc8c0495409a7cba7e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_dhx2_passwd.c,v 1.8 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
index 4c7d87c0cbf03fc4e592134ea52046ba382b994b..bc19c7851b294f7eb151f1a384db4c23e23465e5 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_dhx_pam.c,v 1.33 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -80,7 +79,11 @@ static char *PAM_password;
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) {
   int count = 0;
@@ -193,7 +196,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     DH *dh;
 
     /* get the client's public key */
-    if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+    if (!(bn = BN_bin2bn((unsigned char *)ibuf, KEYSIZE, NULL))) {
     /* Log Entry */
            LOG(log_info, logtype_uams, "uams_dhx_pam.c :PAM No Public Key -- %s",
                  strerror(errno));
@@ -255,10 +258,10 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     }
 
     /* figure out the key. store the key in rbuf for now. */
-    i = DH_compute_key(rbuf, bn, dh);
+    i = DH_compute_key((unsigned char *)rbuf, bn, dh);
     
     /* set the key */
-    CAST_set_key(&castkey, i, rbuf);
+    CAST_set_key(&castkey, i, (unsigned char *)rbuf);
     
     /* session id. it's just a hashed version of the object pointer. */
     sessid = dhxhash(obj);
@@ -267,7 +270,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
     *rbuflen += sizeof(sessid);
     
     /* public key */
-    BN_bn2bin(dh->pub_key, rbuf); 
+    BN_bn2bin(dh->pub_key, (unsigned char *)rbuf); 
     rbuf += KEYSIZE;
     *rbuflen += KEYSIZE;
 
@@ -301,7 +304,7 @@ static int dhx_setup(void *obj, char *ibuf, size_t ibuflen _U_,
 #endif /* 0 */
 
     /* encrypt using cast */
-    CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv, 
+    CAST_cbc_encrypt((unsigned char *)rbuf, (unsigned char *)rbuf, CRYPTBUFLEN, &castkey, msg2_iv, 
                     CAST_ENCRYPT);
     *rbuflen += CRYPTBUFLEN;
     BN_free(bn);
@@ -436,13 +439,13 @@ static int pam_logincont(void *obj, struct passwd **uam_pwd,
        hostname = NULL;
        }
 
-    CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+    CAST_cbc_encrypt((unsigned char *)ibuf, (unsigned char *)rbuf, CRYPT2BUFLEN, &castkey,
                     msg3_iv, CAST_DECRYPT);
     memset(&castkey, 0, sizeof(castkey));
 
     /* check to make sure that the random number is the same. we
      * get sent back an incremented random number. */
-    if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+    if (!(bn1 = BN_bin2bn((unsigned char *)rbuf, KEYSIZE, NULL)))
       return AFPERR_PARAM;
 
     if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
@@ -613,13 +616,13 @@ static int pam_changepw(void *obj, char *username,
     }
 
     /* grab the client's nonce, old password, and new password. */
-    CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
+    CAST_cbc_encrypt((unsigned char *)ibuf, (unsigned char *)ibuf, CHANGEPWBUFLEN, &castkey,
                     msg3_iv, CAST_DECRYPT);
     memset(&castkey, 0, sizeof(castkey));
 
     /* check to make sure that the random number is the same. we
      * get sent back an incremented random number. */
-    if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+    if (!(bn1 = BN_bin2bn((unsigned char *)ibuf, KEYSIZE, NULL))) {
     /* Log Entry */
            LOG(log_info, logtype_uams, "uams_dhx_pam.c :PAM: Random Number Not the same or not incremented-- %s",
                  strerror(errno));
index f8cfc1af1434c0ad7384456e9340fe5fdd38d379..29ea36c04842ebf85c8aeb87195ad407eb54b952 100644 (file)
@@ -318,8 +318,6 @@ static int do_gss_auth(void *obj,
 
 cleanup_client_name:
     gss_release_name(&status, &client_name);
-
-cleanup_context:
     gss_release_buffer(&status, &authenticator_buff);
     gss_delete_sec_context(&status, &context, NULL);
 
index 00f90ece0972ee01670d5cc1eee55879d6795b35..82ee43ef5d552dc3fe7fc78441942c125a368bd0 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_guest.c,v 1.18 2009-11-08 01:07:17 didg Exp $
  *
  * (c) 2001 (see COPYING)
  */
index 6f15ac0a30c660bbdec3939e66b0059e06643f5f..b490c2ee20aaf81cd5d5f61888a49a03680e4cc2 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_pam.c,v 1.24 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -54,7 +53,11 @@ extern UAM_MODULE_EXPORT void append(struct papfile *, const char *, int);
  * echo off means password.
  */
 static int PAM_conv (int num_msg,
+#ifdef LINUX
                      const struct pam_message **msg,
+#else
+                     struct pam_message **msg,
+#endif
                      struct pam_response **resp,
                      void *appdata_ptr _U_) 
 {
index 9b9c1d801dd57d53c543d8cdc1e11fb555f18f05..6a2994401c493d5cadd33a5f86161904d17f644c 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: uams_pgp.c,v 1.12 2009-10-15 11:39:48 didg Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
index 301fdd0ff4cb69d48fe7e1f81d7572a954c7dfea..22687fcc8921cf74304f23791390580c326b8b2e 100644 (file)
@@ -1,5 +1,4 @@
 /* 
- * $Id: uams_randnum.c,v 1.21 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
index 4ee8385436cbec5213219bccb5d72809e7b024c4..d935e1e696431facdc09f3910ad194e8805faca7 100644 (file)
@@ -1,4 +1,4 @@
 Makefile
 Makefile.in
 *.o
-lockrpc.gen.h
\ No newline at end of file
+afp_dtrace.h
index 93d28929a0f96d2e7cbc385c5255377a30846297..42085187de9665cefac9ca580b6c2ce541b59270 100644 (file)
@@ -1,6 +1,8 @@
 # Makefile.am for include/atalk/
 
 atalkincludedir = $(includedir)/atalk
+BUILT_SOURCES = 
+CLEANFILES =
 
 atalkinclude_HEADERS = \
        adouble.h \
@@ -40,4 +42,13 @@ noinst_HEADERS = \
        ftw.h \
        dsi.h \
        ldapconfig.h \
-       fce_api.h
\ No newline at end of file
+       fce_api.h
+
+EXTRA_DIST = afp_dtrace.d
+
+if WITH_DTRACE
+BUILT_SOURCES += afp_dtrace.h
+CLEANFILES += afp_dtrace.h
+afp_dtrace.h: $(top_srcdir)/include/atalk/afp_dtrace.d
+       $(LIBTOOL) --mode=execute dtrace -o afp_dtrace.h -h -s $(top_srcdir)/include/atalk/afp_dtrace.d
+endif
index 95aa0753401bb3385013192c3ea620a1fdd327b1..d30a25c1d72168123f40ed4e92963c97a2acded0 100644 (file)
@@ -21,6 +21,8 @@
 
 #ifdef HAVE_ACLS
 
+#define O_NETATALK_ACL (O_NOFOLLOW << 1)
+
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 
@@ -51,6 +53,7 @@ extern int remove_acl_vfs(const char *name);
 
 #else /* HAVE_ACLS=no */
 
+#define O_NETATALK_ACL 0
 #define chmod_acl chmod
 
 #endif /* HAVE_ACLS */
index 384486ea4633d8c14085af8fb998e7853e789e3f..c0f4098da7b3590da71d01a3c92f44b6a9f573c4 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
@@ -219,7 +219,7 @@ struct adouble {
     int                 ad_reso_refcount;
     off_t               ad_rlen;           /* ressource fork len with AFP 3.0         *
                                             * the header parameter size is too small. */
-    char                *ad_name;          /* name in server encoding (usually UTF8)  */
+    char                *ad_name;          /* name (UTF8-MAC)                         */
     struct adouble_fops *ad_ops;
     uint16_t            ad_open_forks;     /* open forks (by others)                  */
     char                ad_data[AD_DATASZ_MAX];
@@ -245,8 +245,10 @@ struct adouble {
 #define ADFLAGS_TRUNC     (1<<12) /* truncate, open called with O_TRUNC */
 
 #define ADVOL_NODEV      (1 << 0)
+#define ADVOL_RO         (1 << 1)
 #define ADVOL_UNIXPRIV   (1 << 2) /* adouble unix priv */
 #define ADVOL_INVDOTS    (1 << 3) /* dot files (.DS_Store) are invisible) */
+#define ADVOL_FOLLO_SYML (1 << 4)
 
 /* lock flags */
 #define ADLOCK_CLR      (0)
@@ -369,6 +371,8 @@ struct adouble {
 #define ad_ref(ad)   (ad)->ad_refcount++
 #define ad_unref(ad) --((ad)->ad_refcount)
 
+#define ad_get_syml_opt(ad) (((ad)->ad_options & ADVOL_FOLLO_SYML) ? 0 : O_NOFOLLOW)
+
 /* ad_flush.c */
 extern int ad_rebuild_adouble_header_v2(struct adouble *);
 extern int ad_rebuild_adouble_header_ea(struct adouble *);
@@ -406,6 +410,7 @@ extern int ad_metadata    (const char *, int, struct adouble *);
 extern int ad_metadataat  (int, const char *, int, struct adouble *);
 extern mode_t ad_hf_mode(mode_t mode);
 extern int ad_valid_header_osx(const char *path);
+extern off_t ad_reso_size(const char *path, int adflags, struct adouble *ad);
 
 /* ad_conv.c */
 extern int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath);
@@ -418,7 +423,7 @@ extern ssize_t ad_write(struct adouble *, uint32_t, off_t, int, const char *, si
 extern ssize_t adf_pread(struct ad_fd *, void *, size_t, off_t);
 extern ssize_t adf_pwrite(struct ad_fd *, const void *, size_t, off_t);
 extern int     ad_dtruncate(struct adouble *, off_t);
-extern int     ad_rtruncate(struct adouble *, off_t);
+extern int     ad_rtruncate(struct adouble *, const char *, off_t);
 extern int     copy_fork(int eid, struct adouble *add, struct adouble *ads);
 
 /* ad_size.c */
diff --git a/include/atalk/afp_dtrace.d b/include/atalk/afp_dtrace.d
new file mode 100644 (file)
index 0000000..e9ee989
--- /dev/null
@@ -0,0 +1,10 @@
+provider afp {
+   probe afpfunc__start(int func, char *funcname);
+   probe afpfunc__done(int func, char *funcname);
+   probe read__start(long size);
+   probe read__done();
+   probe write__start(long size);
+   probe write__done();
+   probe cnid__start(char *cnidfunc);
+   probe cnid__done();
+};
index 2d3aa062b8b601c4ac016e4aa59d5d672e8e05c6..ba0b62fad3d0d1e4149d5aeae63565e1b748e3f8 100644 (file)
@@ -69,6 +69,7 @@ struct _cnid_db {
                                 const char *, size_t, cnid_t);
     int    (*cnid_find)        (struct _cnid_db *cdb, const char *name, size_t namelen,
                                 void *buffer, size_t buflen);
+    int    (*cnid_wipe)        (struct _cnid_db *cdb);
 };
 typedef struct _cnid_db cnid_db;
 
@@ -123,6 +124,7 @@ cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_
                         char *name, const size_t len, cnid_t hint);
 int    cnid_find       (struct _cnid_db *cdb, const char *name, size_t namelen,
                         void *buffer, size_t buflen);
+int    cnid_wipe       (struct _cnid_db *cdb);
 void   cnid_close      (struct _cnid_db *db);
 
 #endif
index d7484e42088d7c69ab71326f96d71c1d093bea86..b61f7a8ce6e3d1d65275c4c01537ac967c6a3894 100644 (file)
@@ -25,6 +25,7 @@
 #define CNID_DBD_OP_GETSTAMP    0x0b
 #define CNID_DBD_OP_REBUILD_ADD 0x0c
 #define CNID_DBD_OP_SEARCH      0x0d
+#define CNID_DBD_OP_WIPE        0x0e
 
 #define CNID_DBD_RES_OK            0x00
 #define CNID_DBD_RES_NOTFOUND      0x01
index 7a910524699446d41745e4b5b6fb52aa0503e723..b28d736e09855bed7cfee85c8d1d464bb8434c97 100644 (file)
@@ -4,7 +4,6 @@
    @file    dictionary.h
    @author  N. Devillard
    @date    Sep 2007
-   @version $Revision: 1.12 $
    @brief   Implements a dictionary for string variables.
 
    This module implements a simple dictionary object, i.e. a list
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $
        $Author: ndevilla $
        $Date: 2007-11-23 21:37:00 $
-       $Revision: 1.12 $
 */
 
 #ifndef _DICTIONARY_H_
index 0e776c31cca07667a43926f5ad96d493163a25ba..fc1af468ac3156934f4ce1771bb19e7ed18591b4 100644 (file)
@@ -138,7 +138,7 @@ typedef struct DSI {
 #define DSI_DEFQUANT        2           /* default attention quantum size */
 #define DSI_SERVQUANT_MAX   0xffffffff  /* server quantum */
 #define DSI_SERVQUANT_MIN   32000       /* minimum server quantum */
-#define DSI_SERVQUANT_DEF   0x0004A2E0L /* default server quantum */
+#define DSI_SERVQUANT_DEF   0x100000L   /* default server quantum (1 MB) */
 
 /* default port number */
 #define DSI_AFPOVERTCP_PORT 548
@@ -154,17 +154,15 @@ typedef struct DSI {
 #define DSI_RECONSOCKET      (1 << 7) /* we have a new socket from primary reconnect */
 #define DSI_RECONINPROG      (1 << 8) /* used in the new session in reconnect */
 #define DSI_AFP_LOGGED_OUT   (1 << 9) /* client called afp_logout, quit on next EOF from socket */
-#if 0
-#define DSI_GOT_ECONNRESET   (1 << 10) /* got ECONNRESET from client => exit */
-#endif
 
 /* basic initialization: dsi_init.c */
 extern DSI *dsi_init(AFPObj *obj, const char *hostname, const char *address, const char *port);
 extern void dsi_setstatus (DSI *, char *, const size_t);
 extern int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address, const char *port);
+extern void dsi_free(DSI *dsi);
 
 /* in dsi_getsess.c */
-extern int dsi_getsession (DSI *, server_child *, const int, afp_child_t **);
+extern int dsi_getsession (DSI *, server_child_t *, const int, afp_child_t **);
 extern void dsi_kill (int);
 
 
index 094065b9d5a7d6a799680eba3f2eac91c197e65a..ccc66eb6f4ac1a9ae7ff52a57c3291d219e3ed02 100644 (file)
@@ -17,6 +17,7 @@
 
 #define EC_INIT int ret = 0
 #define EC_STATUS(a) ret = (a)
+#define EC_EXIT_STATUS(a) do { ret = (a); goto cleanup; } while (0)
 #define EC_FAIL do { ret = -1; goto cleanup; } while (0)
 #define EC_FAIL_LOG(a, ...)                     \
     do {               \
index aebb56adffcdafdb9463b712afa9fe91685186e0..d11a2d4927a4752da0593fb3e54c2acc976cf57e 100755 (executable)
 #define FCE_DIR_DELETE      3
 #define FCE_FILE_CREATE     4
 #define FCE_DIR_CREATE      5
-#define FCE_TM_SIZE         6
 #define FCE_CONN_START     42
 #define FCE_CONN_BROKEN    99
 
+#define FCE_FIRST_EVENT     FCE_FILE_MODIFY /* keep in sync with last file event above */
+#define FCE_LAST_EVENT      FCE_DIR_CREATE  /* keep in sync with last file event above */
 
 /* fce_packet.fce_magic */
 #define FCE_PACKET_MAGIC  "at_fcapi"
@@ -31,6 +32,7 @@
  * Format is network byte order.
  */
 #define FCE_PACKET_HEADER_SIZE 8+1+1+4+2
+
 struct fce_packet
 {
     char magic[8];
@@ -41,21 +43,17 @@ struct fce_packet
     char data[MAXPATHLEN];
 };
 
+typedef uint32_t fce_ev_t;
+typedef enum { fce_file, fce_dir } fce_obj_t;
+
 struct path;
 struct ofork;
 
 void fce_pending_events(AFPObj *obj);
-
-int fce_register_delete_file( struct path *path );
-int fce_register_delete_dir( char *name );
-int fce_register_new_dir( struct path *path );
-int fce_register_new_file( struct path *path );
-int fce_register_file_modification( struct ofork *ofork );
-int fce_register_tm_size(const char *vol, size_t used);
-
+int fce_register(fce_ev_t event, const char *path, const char *oldpath, fce_obj_t type);
 int fce_add_udp_socket(const char *target );  // IP or IP:Port
 int fce_set_coalesce(const char *coalesce_opt ); // all|delete|create
-int fce_set_events(const char *events);     /* fmod,fdel,ddel,fcre,dcre,tmsz (default is all except tmsz) */
+int fce_set_events(const char *events);     /* fmod,fdel,ddel,fcre,dcre */
 
 #define FCE_DEFAULT_PORT 12250
 #define FCE_DEFAULT_PORT_STRING "12250"
index b8412d15d7c7b300f8b90b3c94a58ddeb96e374b..b245601375afd4dae9f5d715d8050eed045eeb6f 100644 (file)
 #include <atalk/unicode.h>
 #include <atalk/uam.h>
 #include <atalk/iniparser.h>
+#ifdef WITH_DTRACE
+#include <atalk/afp_dtrace.h>
+#else
+/* List of empty dtrace macros */
+#define AFP_AFPFUNC_START(a,b)
+#define AFP_AFPFUNC_DONE(a, b)
+#define AFP_CNID_START(a)
+#define AFP_CNID_DONE()
+#define AFP_READ_START(a)
+#define AFP_READ_DONE()
+#define AFP_WRITE_START(a)
+#define AFP_WRITE_DONE()
+#endif
 
 /* #define DOSFILELEN 12 */             /* Type1, DOS-compat*/
 #define MACFILELEN 31                   /* Type2, HFS-compat */
@@ -41,8 +54,9 @@
 #define OPTION_UUID          (1 << 7)
 #define OPTION_ACL2MACCESS   (1 << 8)
 #define OPTION_NOZEROCONF    (1 << 9)
-#define OPTION_KEEPSESSIONS  (1 << 10) /* preserve sessions across master afpd restart with SIGQUIT */
+#define OPTION_ACL2MODE      (1 << 10)
 #define OPTION_SHARE_RESERV  (1 << 11) /* whether to use Solaris fcntl F_SHARE locks */
+#define OPTION_DBUS_AFPSTATS (1 << 12) /* whether to run dbus thread for afpstats */
 
 #define PASSWD_NONE     0
 #define PASSWD_SET     (1 << 0)
@@ -80,13 +94,13 @@ struct afp_options {
     uint32_t server_quantum;
     int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
     char *hostname;
-    char *listen, *port;
+    char *listen, *interfaces, *port;
     char *Cnid_srv, *Cnid_port;
     char *configfile;
     char *uampath, *fqdn;
     char *sigconffile;
     char *uuidconf;
-    char *guest, *loginmesg, *keyfile, *passwdfile;
+    char *guest, *loginmesg, *keyfile, *passwdfile, *extmapfile;
     char *uamlist;
     char *signatureopt;
     unsigned char signature[16];
@@ -98,7 +112,7 @@ struct afp_options {
     gid_t admingid;
     int    volnamelen;
     /* default value for winbind authentication */
-    char *ntdomain, *ntseparator;
+    char *ntdomain, *ntseparator, *addomain;
     char *logconfig;
     char *logfile;
     char *mimicmodel;
@@ -147,14 +161,9 @@ extern const char         *Cnid_port;
 extern int  get_afp_errno   (const int param);
 extern void afp_options_init (struct afp_options *);
 extern void afp_options_parse_cmdline(AFPObj *obj, int ac, char **av);
-extern void afp_options_free(struct afp_options *);
 extern void setmessage (const char *);
 extern void readmessage (AFPObj *);
 
-/* gettok.c */
-extern void initline   (int, char *);
-extern int  parseline  (int, char *);
-
 /* afp_util.c */
 extern const char *AfpNum2name (int );
 extern const char *AfpErr2name(int err);
index 14cddf669d671287020c6d24585106907789bbdd..0a75e4f64f9ccd5f3265769d4dc30f13c660ec91 100644 (file)
@@ -14,7 +14,6 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.h,v 1.2 2009-11-19 10:37:44 franklahm Exp $
  * $Name:  $
  */
 
index 0eefa4cb3aa64c8603d985d60741d7ff09ffe0a4..8f94c5ca4a11f677c135a12794cbbff9be2114b4 100644 (file)
@@ -10,8 +10,6 @@
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: iniparser.h,v 1.26 2011-03-02 20:15:13 ndevilla Exp $
-       $Revision: 1.26 $
 */
 
 #ifndef _INIPARSER_H_
index 16e5484f6dd24822874f465a221918bac251ab9d..adc2a99d441ad31414b6ea392800db191a790c50 100644 (file)
@@ -7,6 +7,7 @@
 
 /* One function does the whole job */
 extern int acl_ldap_readconfig(dictionary *iniconfig);
+extern void acl_ldap_freeconfig(void);
 
 /* These are the prefvalues */
 extern char *ldap_server;
@@ -33,6 +34,7 @@ struct ldap_pref {
     int strorint;     /* string to just store in char * or convert to int ? */
     int intfromarray; /* convert to int, but use string to int mapping array pref_array[] */
     int valid;        /* -1 = mandatory, 0 = omittable/valid */
+    int valid_save;   /* copy of 'valid', used when resettting config */
 };
 
 struct pref_array {
index cf67bcad6f5260d8b1e0d42b3812e77a9b492521..1847c0d50b8ee746e583b749b3e8ec02e683f5f8 100644 (file)
@@ -90,9 +90,9 @@ enum logtypes {
   logtype_cnid,
   logtype_afpd,
   logtype_dsi,
-  logtype_atalkd,
-  logtype_papd,
   logtype_uams,
+  logtype_fce,
+  logtype_ad,
   logtype_end_of_list_marker  /* don't put any logtypes after this */
 };
 
index 33459b3188b70a4c1e3732bcc5a2b808e9000d77..858b4f1cd17a1107dbd017023c9c8ef77b2f7f28 100644 (file)
@@ -21,9 +21,9 @@
 #include <atalk/volume.h>
 
 extern int        afp_config_parse(AFPObj *obj, char *processname);
-
+extern void       afp_config_free(AFPObj *obj);
 extern int        load_charset(struct vol *vol);
-extern int        load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *));
+extern int        load_volumes(AFPObj *obj);
 extern void       unload_volumes(AFPObj *obj);
 extern struct vol *getvolumes(void);
 extern struct vol *getvolbyvid(const uint16_t);
@@ -31,4 +31,9 @@ extern struct vol *getvolbypath(AFPObj *obj, const char *path);
 extern struct vol *getvolbyname(const char *name);
 extern void       volume_free(struct vol *vol);
 extern void       volume_unlink(struct vol *volume);
+
+/* Extension type/creator mapping */
+struct extmap *getdefextmap(void);
+struct extmap *getextmap(const char *path);
+
 #endif
index 1aab969094f0c22cb48b328abfa249ccdc3a4a89..f5f6486cfb634d0e585cef21923c7bd0c3e0928b 100644 (file)
 #define ATALKPATHCAT(a,b) a/**/b
 #endif
 
-
-/* lock file path. this should be re-organized a bit. */
-#if ! defined (_PATH_LOCKDIR)
-#  if defined (FHS_COMPATIBILITY) || defined (__NetBSD__) || defined (__OpenBSD__)
-#    define _PATH_LOCKDIR      "/var/run/"
-#  elif defined (BSD4_4)
-#    ifdef MACOSX_SERVER
-#      define _PATH_LOCKDIR    "/var/run/"
-#    else
-#      define _PATH_LOCKDIR    "/var/spool/lock/"
-#    endif
-#  elif defined (linux)
-#    define _PATH_LOCKDIR      "/var/lock/"
-#  else
-#    define _PATH_LOCKDIR      "/var/spool/locks/"
-#  endif
-#endif
-
-
-/*
- * netatalk paths
- */
-#define _PATH_AFPTKT           "/tmp/AFPtktXXXXXX"
-#define _PATH_AFP_IPC       ATALKPATHCAT(_PATH_LOCKDIR,"afpd_ipc")
-#if defined (FHS_COMPATIBILITY) || defined (__NetBSD__) || defined (__OpenBSD__)
-#  define _PATH_NETATALK_LOCK  ATALKPATHCAT(_PATH_LOCKDIR,"netatalk.pid")
-#else
-#  define _PATH_NETATALK_LOCK  ATALKPATHCAT(_PATH_LOCKDIR,"netatalk")
-#endif
-
 #endif /* atalk/paths.h */
 
index ad41bceccec2441f95ee4907547e1b93d39ce4ba..27ee305c7c85037058c4b04b655bc62a0a497d34 100644 (file)
@@ -8,45 +8,50 @@
 
 #include <sys/types.h>
 #include <arpa/inet.h>
+#include <pthread.h>
 
 /* useful stuff for child processes. most of this is hidden in 
  * server_child.c to ease changes in implementation */
 
-#define CHILD_NFORKS   2
-#define CHILD_ASPFORK  0
-#define CHILD_PAPFORK  0
-#define CHILD_DSIFORK  1
-
-typedef struct server_child {
-  void *fork;
-  int count, nsessions, nforks;
-} server_child;
-
-typedef struct server_child_data {
-  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
-  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
-  int       valid;             /* 1 if we have a clientid */
-  int       killed;            /* 1 if we already tried to kill the client */
-  int       disasociated; /* 1 if this is not a child, but a child from a previous afpd master */
-  uint32_t  time;              /* client boot time (from the mac client) */
-  uint32_t  idlen;             /* clientid len (from the Mac client) */
-  char      *clientid;  /* clientid (from the Mac client) */
-  int       ipc_fd; /* socket for IPC bw afpd parent and childs */
-  struct server_child_data **prevp, *next;
+#define CHILD_HASHSIZE 32
+
+/* One AFP session child process */
+typedef struct afp_child {
+    pid_t           afpch_pid;         /* afpd worker process pid (from the worker afpd process )*/
+    uid_t           afpch_uid;         /* user id of connected client (from the worker afpd process) */
+    int             afpch_valid;       /* 1 if we have a clientid */
+    int             afpch_killed;      /* 1 if we already tried to kill the client */
+    uint32_t        afpch_boottime;    /* client boot time (from the mac client) */
+    time_t          afpch_logintime;   /* time the child was added */
+    uint32_t        afpch_idlen;       /* clientid len (from the Mac client) */
+    char           *afpch_clientid;    /* clientid (from the Mac client) */
+    int             afpch_ipc_fd;      /* socket for IPC bw afpd parent and childs */
+    int16_t         afpch_state;       /* state of AFP session (eg active, sleeping, disconnected) */
+    char           *afpch_volumes;     /* mounted volumes */
+    struct afp_child **afpch_prevp;
+    struct afp_child *afpch_next;
 } afp_child_t;
 
+/* Info and table with all AFP session child processes */
+typedef struct {
+    pthread_mutex_t servch_lock;                    /* Lock */
+    int             servch_count;                   /* Current count of active AFP sessions */
+    int             servch_nsessions;               /* Number of allowed AFP sessions */
+    afp_child_t    *servch_table[CHILD_HASHSIZE];   /* Hashtable with data of AFP sesssions */
+} server_child_t;
+
 /* server_child.c */
-extern server_child *server_child_alloc (const int, const int);
-extern afp_child_t *server_child_add (server_child *, int, pid_t, int ipc_fd);
-extern int  server_child_remove (server_child *, const int, const pid_t);
-extern void server_child_free (server_child *);
-
-extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
-                                               const uint32_t len, char *id, uint32_t boottime);
-extern int  server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
-extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
-extern void server_child_handler (server_child *);
-extern void server_reset_signal (void);
+extern server_child_t *server_child_alloc(int);
+extern afp_child_t *server_child_add(server_child_t *, pid_t, int ipc_fd);
+extern int  server_child_remove(server_child_t *, pid_t);
+extern void server_child_free(server_child_t *);
+extern afp_child_t *server_child_resolve(server_child_t *childs, id_t pid);
+
+extern void server_child_kill(server_child_t *, int);
+extern void server_child_kill_one_by_id(server_child_t *children, pid_t pid, uid_t,
+                                        uint32_t len, char *id, uint32_t boottime);
+extern int  server_child_transfer_session(server_child_t *children, pid_t, uid_t, int, uint16_t);
+extern void server_child_handler(server_child_t *);
+extern void server_reset_signal(void);
 
 #endif
index aca04f80f8f33ea6b180083c252452836c21caff..0c38a7b75b7896bee929bedbffa24a476e0b73ec 100644 (file)
@@ -4,13 +4,14 @@
 #include <atalk/server_child.h>
 #include <atalk/globals.h>
 
+/* Remember to add IPC commands to server_ipc.c:ipc_cmd_str[] */
 #define IPC_DISCOLDSESSION   0
 #define IPC_GETSESSION       1
+#define IPC_STATE            2  /* pass AFP session state */
+#define IPC_VOLUMES          3  /* pass list of open volumes */
 
-extern int ipc_server_uds(const char *name);
-extern int ipc_client_uds(const char *name);
-extern int reconnect_ipc(AFPObj *);
-extern int ipc_server_read(server_child *children, int fd);
+extern int ipc_server_read(server_child_t *children, int fd);
 extern int ipc_child_write(int fd, uint16_t command, int len, void *token);
+extern int ipc_child_state(AFPObj *obj, uint16_t state);
 
 #endif /* IPC_GETSESSION_LOGIN */
index 0bb43b52de51419d59c73c93ed527722793e6d15..75a970b631efa16167194b0108cb33cb5ca05f63 100644 (file)
@@ -56,8 +56,6 @@ typedef struct {
 #define CONV_DECOMPOSE      (1<<7) /* precompose */
 #define CONV_FORCE          (1<<8) /* force convertion */
 #define CONV__EILSEQ        (1<<9) /* ignore EILSEQ, replace with IGNORE_CHAR (try USC2) */
-#define CONV_ALLOW_COLON   (1<<10) /* Allow ':' in name. Needed for Extended Attributes */
-#define CONV_ALLOW_SLASH   (1<<11) /* Allow '/' in name. Needed for volume name */
 
 /* conversion return flags */
 #define CONV_REQMANGLE  (1<<14) /* mangling of returned name is required */
@@ -125,6 +123,7 @@ extern size_t   utf8_strlen_validate ( char *);
 
 /* from charcnv.c */
 extern int      set_charset_name(charset_t, const char *);
+extern void     free_charset_names(void);
 extern void     init_iconv (void);
 extern size_t   convert_string (charset_t, charset_t, void const *, size_t, void *, size_t);
 extern size_t   convert_string_allocate (charset_t, charset_t, void const *, size_t, char **);
index 01997e9400f8b74d5e4c77a6a45c4f036029d4b0..c12bf5bc0919f296934cf404435ceedd8b3e9772 100644 (file)
 #include <sys/types.h>
 #include <dirent.h>
 
+#include <atalk/globals.h>
+
 #define NETATALK_DIOSZ_STACK 65536
 #define NETATALK_DIOSZ_HEAP  (1024*1024)
 
+struct vol;
+
 /* vfs/unix.c */
 extern int netatalk_unlink(const char *name);
 extern int netatalk_unlinkat(int dirfd, const char *name);
 extern int statat(int dirfd, const char *path, struct stat *st);
-extern int lstatat(int dirfd, const char *path, struct stat *st);
 extern DIR *opendirat(int dirfd, const char *path);
 
 /* rmdir ENOENT not an error */
 extern int netatalk_rmdir(int dirfd, const char *name);
 extern int netatalk_rmdir_all_errors(int dirfd, const char *name);
 
-extern int setfilmode(const char *, mode_t, struct stat *, mode_t);
+extern int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st);
 extern int dir_rx_set(mode_t mode);
 extern int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath);
 extern int copy_file(int sfd, const char *src, const char *dst, mode_t mode);
@@ -47,5 +50,6 @@ extern int copy_ea(const char *ea, int sfd, const char *src, const char *dst, mo
 extern void become_root(void);
 extern void unbecome_root(void);
 extern int gmem(gid_t gid, int ngroups, gid_t *groups);
-
+extern int set_groups(AFPObj *obj, struct passwd *pwd);
+extern const char *print_groups(int ngroups, gid_t *groups);
 #endif  /* ATALK_UNIX_H */
index c5b23ee6f9eaeca3687c0a884d6b5a0c5c614c63..facc77636a476fdd05c9fffdbe92d0b8c9554c8a 100644 (file)
@@ -15,6 +15,7 @@
 #include <sys/socket.h>
 #include <unistd.h>
 #include <poll.h>
+#include <sys/stat.h>
 
 #include <atalk/unicode.h>
 #include <atalk/bstrlib.h>
@@ -148,7 +149,7 @@ extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
 extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
 
 /* Structures and functions dealing with dynamic pollfd arrays */
-enum fdtype {IPC_FD, LISTEN_FD, DISASOCIATED_IPC_FD};
+enum fdtype {IPC_FD, LISTEN_FD};
 struct polldata {
     enum fdtype fdtype; /* IPC fd or listening socket fd                 */
     void *data;         /* pointer to AFPconfig for listening socket and *
@@ -178,10 +179,18 @@ extern int recv_fd(int fd, int nonblocking);
 extern const char *getcwdpath(void);
 extern const char *fullpathname(const char *);
 extern char *stripped_slashes_basename(char *p);
-extern int lchdir(const char *dir);
 extern void randombytes(void *buf, int n);
 extern int daemonize(int nochdir, int noclose);
 extern int run_cmd(const char *cmd, char **cmd_argv);
+extern char *realpath_safe(const char *path);
+extern const char *basename_safe(const char *path);
+extern char *strtok_quote (char *s, const char *delim);
+
+extern int ochdir(const char *dir, int options);
+extern int ostat(const char *path, struct stat *buf, int options);
+extern int ostatat(int dirfd, const char *path, struct stat *st, int options);
+extern int ochown(const char *path, uid_t owner, gid_t group, int options);
+extern int ochmod(char *path, mode_t mode, const struct stat *st, int options);
 
 /******************************************************************
  * cnid.c
@@ -189,4 +198,11 @@ extern int run_cmd(const char *cmd, char **cmd_argv);
 
 extern bstring rel_path_in_vol(const char *path, const char *volpath);
 
+/******************************************************************
+ * cnid.c
+ *****************************************************************/
+
+extern void initline   (int, char *);
+extern int  parseline  (int, char *);
+
 #endif  /* _ATALK_UTIL_H */
index 116a4ceca336e947c6ea9e21038ab267ddbb7249..b185f994c1e2a0f7b0175a1e28c32735a7e6fb70 100644 (file)
 
 typedef uint64_t VolSpace;
 
+/* This should belong in a file.h */
+struct extmap {
+    char               *em_ext;
+    char               em_creator[4];
+    char               em_type[4];
+};
+
 struct vol {
     struct vol      *v_next;
     AFPObj          *v_obj;
@@ -125,6 +132,7 @@ struct vol {
 #define AFPVOL_ACLS      (1 << 24)   /* Volume supports ACLS */
 #define AFPVOL_SEARCHDB  (1 << 25)   /* Use fast CNID db search instead of filesystem */
 #define AFPVOL_NONETIDS  (1 << 26)   /* signal the client it shall do privelege mapping */
+#define AFPVOL_FOLLOWSYM (1 << 27)   /* follow symlinks on the server, default is not to */
 
 /* Extended Attributes vfs indirection  */
 #define AFPVOL_EA_NONE           0   /* No EAs */
@@ -183,6 +191,6 @@ struct vol {
 #define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
 #define vol_unix_priv(vol) ((vol)->v_obj->afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
 #define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
-
+#define vol_syml_opt(vol) (((vol)->v_flags & AFPVOL_FOLLOWSYM) ? 0 : O_NOFOLLOW)
 
 #endif
index 33a5b662a8d4d689fd366a470abd24c71c971487..e9f48faee67710afcf92d2996c7131409e825e4e 100644 (file)
@@ -6,3 +6,4 @@ Makefile.in
 .libs
 dummy.o
 libatalk.abi.tmp
+*dev.abi
index ed597dcb5f0a608c389079722dd38bff5118cc8d..a6df63bd7f3a2fc9e2ec5f1410df2800fb41c5fa 100644 (file)
@@ -18,7 +18,7 @@
 #        current+1:0:0
 #
 
-VERSION_INFO = 2:0:0
+VERSION_INFO = 5:0:0
 
 # History:          VERSION_INFO
 #
@@ -29,6 +29,9 @@ VERSION_INFO = 2:0:0
 #   3.0.0-beta2     1:0:0
 #   3.0             1:0:0
 #   3.0.1           2:0:0
+#   3.0.2           3:0:0
+#   3.0.3           4:0:0
+#   3.0.4           5:0:0
 
 SUBDIRS = acl adouble bstring compat cnid dsi iniparser tdb util unicode vfs
 
@@ -36,8 +39,11 @@ lib_LTLIBRARIES = libatalk.la
 
 libatalk_la_SOURCES = dummy.c
 
+libatalk_la_CFLAGS = \
+       @PTHREAD_CFLAGS@
+
 libatalk_la_LIBADD  = \
-       @WRAP_LIBS@ @ACL_LIBS@ \
+       @WRAP_LIBS@ @ACL_LIBS@ @PTHREAD_LIBS@ \
        acl/libacl.la \
        adouble/libadouble.la   \
        bstring/libbstring.la \
@@ -88,5 +94,8 @@ endif
 EXTRA_DIST = \
        libatalk-3.0beta1.abi \
        libatalk-3.0beta2.abi \
+       libatalk-3.0.abi \
        libatalk-3.0.1.abi \
-       libatalk-3.0.abi
+       libatalk-3.0.2.abi \
+       libatalk-3.0.3.abi \
+       libatalk-3.0.4.abi
index 0e75ee9ee420ff98c151043d533ab13c2f1150be..1013f46c15ddd55a22f8f5da570235e507a2e5be 100644 (file)
@@ -6,7 +6,7 @@ noinst_LTLIBRARIES = libacl.la
 libacl_la_SOURCES = cache.c unix.c uuid.c
 libacl_la_CFLAGS =
 libacl_la_LDFLAGS =
-libacl_la_LIBADD  =
+libacl_la_LIBADD  = @ACL_LIBS@
 
 if HAVE_LDAP
 libacl_la_SOURCES += ldap.c ldap_config.c
index bc9806d9d323f1e58af3c44e0c94c79b62ad1037..15eea478d099e1b361c69c89a37fefa30a2125cc 100644 (file)
@@ -46,7 +46,6 @@ static cacheduser_t *uuidcache[256];   /* indexed by hash of uuid */
 
 void uuidcache_dump(void) {
     int i;
-    int ret = 0;
     cacheduser_t *entry;
     char timestr[200];
     struct tm *tmp = NULL;
@@ -301,7 +300,6 @@ int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const
     char *name = NULL;
     unsigned char *uuid = NULL;
     cacheduser_t *cacheduser = NULL;
-    cacheduser_t *entry;
     unsigned char hash;
 
     /* allocate mem and copy values */
index 11731a26c69d5bdeb683be1667a8c2c6bd351e03..8e33004a9e9a644778b776c612159f2adcc7ca32 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: cache.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index 8743bafeabcd90b7f0e16afd6fa79bf3738b332a..f04c5aef1ed106df95247757e97581c08ff86ef5 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/time.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
 #define LDAP_DEPRECATED 1
 #include <ldap.h>
 
@@ -56,21 +57,22 @@ char *ldap_uid_attr;
 int  ldap_uuid_encoding;
 
 struct ldap_pref ldap_prefs[] = {
-    {&ldap_server,     "ldap server",      0, 0, -1},
-    {&ldap_auth_method,"ldap auth method", 1, 1, -1},
-    {&ldap_auth_dn,    "ldap auth dn",     0, 0,  0},
-    {&ldap_auth_pw,    "ldap auth pw",     0, 0,  0},
-    {&ldap_userbase,   "ldap userbase",    0, 0, -1},
-    {&ldap_userscope,  "ldap userscope",   1 ,1, -1},
-    {&ldap_groupbase,  "ldap groupbase",   0, 0, -1},
-    {&ldap_groupscope, "ldap groupscope",  1 ,1, -1},
-    {&ldap_uuid_attr,  "ldap uuid attr",   0, 0, -1},
-    {&ldap_uuid_string,"ldap uuid string", 0, 0,  0},
-    {&ldap_name_attr,  "ldap name attr",   0, 0, -1},
-    {&ldap_group_attr, "ldap group attr",  0, 0, -1},
-    {&ldap_uid_attr,   "ldap uid attr",    0, 0,  0},
-    {&ldap_uuid_encoding,"ldap uuid encoding", 1, 1,  0},
-    {NULL,             NULL,               0, 0, -1}
+    /* pointer to pref,    prefname,              strorint, intfromarray, valid, valid_save */
+    {&ldap_server,         "ldap server",         0, 0, -1, -1},
+    {&ldap_auth_method,    "ldap auth method",    1, 1, -1, -1},
+    {&ldap_auth_dn,        "ldap auth dn",        0, 0,  0,  0},
+    {&ldap_auth_pw,        "ldap auth pw",        0, 0,  0,  0},
+    {&ldap_userbase,       "ldap userbase",       0, 0, -1, -1},
+    {&ldap_userscope,      "ldap userscope",      1 ,1, -1, -1},
+    {&ldap_groupbase,      "ldap groupbase",      0, 0, -1, -1},
+    {&ldap_groupscope,     "ldap groupscope",     1 ,1, -1, -1},
+    {&ldap_uuid_attr,      "ldap uuid attr",      0, 0, -1, -1},
+    {&ldap_uuid_string,    "ldap uuid string",    0, 0,  0,  0},
+    {&ldap_name_attr,      "ldap name attr",      0, 0, -1, -1},
+    {&ldap_group_attr,     "ldap group attr",     0, 0, -1, -1},
+    {&ldap_uid_attr,       "ldap uid attr",       0, 0,  0,  0},
+    {&ldap_uuid_encoding,  "ldap uuid encoding",  1, 1,  0,  0},
+    {NULL,                 NULL,                  0, 0,  0,  0}
 };
 
 struct pref_array prefs_array[] = {
@@ -345,7 +347,7 @@ int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
         int i = 0;
         int s = 0;
         char c;
-        while(c = uuidstr[i]) {
+        while ((c = uuidstr[i])) {
             if((c >='a' && c <= 'f')
                 || (c >= 'A' && c <= 'F')
                 || (c >= '0' && c <= '9')) {
index bd9d414b85758ce8439863100e9752f33b0785af..26a63fa9c76dab72f94af1fb812943d50f2ba804 100644 (file)
 #include <atalk/logger.h>
 #include <atalk/iniparser.h>
 
+void acl_ldap_freeconfig(void)
+{
+    for (int i = 0; ldap_prefs[i].name != NULL; i++) {
+        if (ldap_prefs[i].intfromarray == 0 && ldap_prefs[i].strorint == 0) {
+            free(*((char **)(ldap_prefs[i].pref)));
+            *((char **)(ldap_prefs[i].pref)) = NULL;
+        }
+        ldap_prefs[i].valid = ldap_prefs[i].valid_save;
+    }
+}
+
 int acl_ldap_readconfig(dictionary *iniconfig)
 {
     int i, j;
index 6284935173d87c7cced42ce80419977cc1df40f8..cc69d8ade3299bd20fbced73d3c5485b0f4f8132 100644 (file)
@@ -308,6 +308,14 @@ int posix_chmod(const char *name, mode_t mode) {
     /* Call chmod() first because there might be some special bits to be set which
      * aren't related to access control.
      */
+#ifdef BSD4_4
+    /*
+     * On FreeBSD chmod_acl() ends up in here too, but on
+     * FreeBSD sine ~9.1 with ZFS doesn't allow setting the g+s bit.
+     * Fixes PR #491.
+     */
+    mode &= 0777;
+#endif
     ret = chmod(name, mode);
 
     if (ret)
index b71c13e689cd3cab889cbb6a24dfb7bbf1c45417..ff58c3dd2202f562f3b757bca7053a85cbfdf4ca 100644 (file)
@@ -209,7 +209,6 @@ int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid) {
         add_cachebyname(name, uuid, mytype, 0);
     }
 
-cleanup:
 #ifdef HAVE_LDAP
     if (uuid_string) free(uuid_string);
 #endif
@@ -255,6 +254,7 @@ int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
         uid = ntohl(tmp);
         if ((pwd = getpwuid(uid)) == NULL) {
             /* not found, add negative entry to cache */
+            *name = NULL;
             add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
             ret = -1;
         } else {
@@ -264,7 +264,7 @@ int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
         }
         LOG(log_debug, logtype_afpd,
             "getnamefromuuid{local}: UUID: %s -> name: %s, type:%s",
-            uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+            uuid_bin2string(uuidp), *name ? *name : "-", uuidtype[(*type) & UUIDTYPESTR_MASK]);
         return ret;
     } else if (memcmp(uuidp, local_group_uuid, 12) == 0) {
         *type = UUID_GROUP;
index e5fe323fd7814249cf21b412695a85f02739f3ae..43d798bb647f7e0f6921a22ec33652b822bc204b 100644 (file)
@@ -117,25 +117,22 @@ int ad_setid (struct adouble *adp, const dev_t dev, const ino_t ino , const uint
 }
 
 /* ----------------------------- */
-uint32_t ad_getid (struct adouble *adp, const dev_t st_dev, const ino_t st_ino , const cnid_t did, const void *stamp)
+uint32_t ad_getid (struct adouble *adp, const dev_t st_dev, const ino_t st_ino , const cnid_t did, const void *stamp _U_)
 {
     uint32_t aint = 0;
     dev_t  dev;
     ino_t  ino;
     cnid_t a_did;
-    char   temp[ADEDLEN_PRIVSYN];
 
     if (adp) {
         if (sizeof(dev_t) == ad_getentrylen(adp, ADEID_PRIVDEV)) {
             memcpy(&dev, ad_entry(adp, ADEID_PRIVDEV), sizeof(dev_t));
             memcpy(&ino, ad_entry(adp, ADEID_PRIVINO), sizeof(ino_t));
-            memcpy(temp, ad_entry(adp, ADEID_PRIVSYN), sizeof(temp));
             memcpy(&a_did, ad_entry(adp, ADEID_DID), sizeof(cnid_t));
 
             if (((adp->ad_options & ADVOL_NODEV) || (dev == st_dev))
                 && ino == st_ino
-                && (!did || a_did == did)
-                && (memcmp(stamp, temp, sizeof(temp)) == 0) ) {
+                && (!did || a_did == did) ) {
                 memcpy(&aint, ad_entry(adp, ADEID_PRIVID), sizeof(aint));
                 if (adp->ad_vers == AD_VERSION2)
                     return aint;
index eaac8db97032eab7aead8b644de35657854ea9f3..b1785c0163f30cf73eb3dde56794e4b6150e7d0e 100644 (file)
@@ -57,15 +57,23 @@ static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struc
     EC_INIT;
     struct adouble adv2;
     struct adouble adea;
-    const char *adpath;
     int adflags;
     uint32_t ctime, mtime, afpinfo = 0;
     char *emptyad;
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
+
+    switch (S_IFMT & sp->st_mode) {
+    case S_IFREG:
+    case S_IFDIR:
+        break;
+    default:
+        return 0;
+    }
 
     ad_init(&adea, vol);
     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
+
     adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
 
     /* Open and lock adouble:v2 file */
@@ -97,12 +105,12 @@ static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struc
             goto copy;
     }
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
     goto EC_CLEANUP;
 
 copy:
     /* Create a adouble:ea meta EA */
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
     EC_ZERO_LOGSTR( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE),
                     "ad_conv_v22ea_hf(\"%s\"): error creating metadata EA: %s",
                     fullpathname(path), strerror(errno));
@@ -112,7 +120,7 @@ copy:
 EC_CLEANUP:
     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
@@ -122,7 +130,14 @@ static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struc
     struct adouble adv2;
     struct adouble adea;
 
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
+
+    switch (S_IFMT & sp->st_mode) {
+    case S_IFREG:
+        break;
+    default:
+        return 0;
+    }
 
     if (S_ISDIR(sp->st_mode))
         return 0;
@@ -142,12 +157,14 @@ static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struc
         EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) );
         adea.ad_rlen = adv2.ad_rlen;
         ad_flush(&adea);
+        fchmod(ad_reso_fileno(&adea), sp->st_mode & 0666);
+        fchown(ad_reso_fileno(&adea), sp->st_uid, sp->st_gid);
     }
 
 EC_CLEANUP:
     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) );
     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
@@ -159,11 +176,14 @@ static int ad_conv_v22ea(const char *path, const struct stat *sp, const struct v
 
     become_root();
 
-    EC_ZERO( ad_conv_v22ea_hf(path, sp, vol) );
-    EC_ZERO( ad_conv_v22ea_rf(path, sp, vol) );
+    if (ad_conv_v22ea_hf(path, sp, vol) != 0)
+        goto delete;
+    if (ad_conv_v22ea_rf(path, sp, vol) != 0)
+        goto delete;
 
+delete:
     EC_NULL( adpath = ad_path(path, adflags) );
-    LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
+    LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
         path, fullpathname(adpath));
 
     unlink(adpath);
@@ -184,21 +204,31 @@ static int ad_conv_dehex(const char *path, const struct stat *sp, const struct v
 {
     EC_INIT;
     static char buf[MAXPATHLEN];
-    const char *adpath, *p;
     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
     bstring newpath = NULL;
+    static bstring str2e = NULL;
+    static bstring str2f = NULL;
+    static bstring strdot = NULL;
+    static bstring strcolon = NULL;
+
+    if (str2e == NULL) {
+        str2e = bfromcstr(":2e");
+        str2f = bfromcstr(":2f");
+        strdot = bfromcstr(".");
+        strcolon = bfromcstr(":");
+    }
 
-    LOG(log_debug, logtype_default,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
 
     *newpathp = NULL;
 
-    if ((p = strchr(path, ':')) == NULL)
+    if (((strstr(path, ":2e")) == NULL) && ((strstr(path, ":2f")) == NULL) )
         goto EC_CLEANUP;
 
     EC_NULL( newpath = bfromcstr(path) );
 
-    EC_ZERO( bfindreplace(newpath, bfromcstr(":2e"), bfromcstr("."), 0) );
-    EC_ZERO( bfindreplace(newpath, bfromcstr(":2f"), bfromcstr(":"), 0) );
+    EC_ZERO( bfindreplace(newpath, str2e, strdot, 0) );
+    EC_ZERO( bfindreplace(newpath, str2f, strcolon, 0) );
     
     become_root();
     if (adflags != ADFLAGS_DIR)
@@ -231,11 +261,14 @@ int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, c
     EC_INIT;
     const char *p;
 
-    LOG(log_debug, logtype_default,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
 
     if (newpath)
         *newpath = NULL;
 
+    if (vol->v_flags & AFPVOL_RO)
+        EC_EXIT_STATUS(0);
+
     if ((vol->v_adouble == AD_VERSION_EA) && !(vol->v_flags & AFPVOL_NOV2TOEACONV))
         EC_ZERO( ad_conv_v22ea(path, sp, vol) );
 
@@ -246,7 +279,7 @@ int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, c
     }
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
     EC_EXIT;
 }
 
index a637b27f2ebeb108db55d5eeeb9674684e43beb2..50fd8baef2a2acc7297d0dbb5178490cc456532f 100644 (file)
@@ -59,9 +59,8 @@ int ad_rebuild_adouble_header_v2(struct adouble *ad)
     uint32_t       temp;
     uint16_t       nent;
     char        *buf, *nentp;
-    int             len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_v2");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_v2");
 
     buf = ad->ad_data;
 
@@ -105,9 +104,8 @@ int ad_rebuild_adouble_header_ea(struct adouble *ad)
     uint32_t       temp;
     uint16_t       nent;
     char        *buf, *nentp;
-    int             len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_ea");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_ea");
 
     buf = ad->ad_data;
 
@@ -153,9 +151,8 @@ static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
     uint32_t       temp;
     uint16_t       nent;
     char           *buf;
-    int            len;
 
-    LOG(log_debug, logtype_default, "ad_rebuild_adouble_header_osx");
+    LOG(log_debug, logtype_ad, "ad_rebuild_adouble_header_osx");
 
     buf = &adbuf[0];
 
@@ -167,7 +164,7 @@ static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
     memcpy(buf, &temp, sizeof( temp ));
     buf += sizeof( temp );
 
-    memset(buf, 0, sizeof(ad->ad_filler));
+    memcpy(buf, "Netatalk        ", 16);
     buf += sizeof( ad->ad_filler );
 
     nent = htons(ADEID_NUM_OSX);
@@ -237,8 +234,8 @@ int ad_copy_header(struct adouble *add, struct adouble *ads)
     }
     add->ad_rlen = ads->ad_rlen;
 
-    if ((ads->ad_vers == AD_VERSION2) && (add->ad_vers = AD_VERSION_EA)
-        || (ads->ad_vers == AD_VERSION_EA) && (add->ad_vers = AD_VERSION2)) {
+    if (((ads->ad_vers == AD_VERSION2) && (add->ad_vers == AD_VERSION_EA))
+        || ((ads->ad_vers == AD_VERSION_EA) && (add->ad_vers == AD_VERSION2))) {
         cnid_t id;
         memcpy(&id, ad_entry(add, ADEID_PRIVID), sizeof(cnid_t));
         id = htonl(id);
@@ -253,7 +250,7 @@ static int ad_flush_hf(struct adouble *ad)
     int len;
     int cwd = -1;
 
-    LOG(log_debug, logtype_default, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush_hf(%s)", adflags2logstr(ad->ad_adflags));
 
     struct ad_fd *adf;
 
@@ -265,7 +262,7 @@ static int ad_flush_hf(struct adouble *ad)
         adf = &ad->ad_data_fork;
         break;
     default:
-        LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+        LOG(log_error, logtype_ad, "ad_flush: unexpected adouble version");
         return -1;
     }
 
@@ -292,7 +289,8 @@ static int ad_flush_hf(struct adouble *ad)
                 if (ad->ad_adflags & ADFLAGS_DIR) {
                     EC_NEG1_LOG( cwd = open(".", O_RDONLY) );
                     EC_NEG1_LOG( fchdir(ad_data_fileno(ad)) );
-                    EC_ZERO_LOG( sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) );
+                    EC_ZERO_LOGSTR( sys_lsetxattr(".", AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0),
+                                    "sys_lsetxattr(\"%s\"): %s", fullpathname(".") ,strerror(errno));
                     EC_NEG1_LOG( fchdir(cwd) );
                     EC_NEG1_LOG( close(cwd) );
                     cwd = -1;
@@ -302,7 +300,7 @@ static int ad_flush_hf(struct adouble *ad)
             }
             break;
         default:
-            LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+            LOG(log_error, logtype_ad, "ad_flush: unexpected adouble version");
             return -1;
         }
     }
@@ -329,7 +327,7 @@ static int ad_flush_rf(struct adouble *ad)
     if (ad->ad_vers != AD_VERSION_EA)
         return 0;
 
-    LOG(log_debug, logtype_default, "ad_flush_rf(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush_rf(%s)", adflags2logstr(ad->ad_adflags));
 
     if ((ad->ad_rfp->adf_flags & O_RDWR)) {
         if (ad_getentryoff(ad, ADEID_RFORK)) {
@@ -353,7 +351,7 @@ int ad_flush(struct adouble *ad)
 {
     EC_INIT;
 
-    LOG(log_debug, logtype_default, "ad_flush(%s)", adflags2logstr(ad->ad_adflags));
+    LOG(log_debug, logtype_ad, "ad_flush(%s)", adflags2logstr(ad->ad_adflags));
 
     if (AD_META_OPEN(ad)) {
         EC_ZERO( ad_flush_hf(ad) );
@@ -393,7 +391,7 @@ int ad_close(struct adouble *ad, int adflags)
         return err;
 
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_close(%s): BEGIN: {d: %d, m: %d, r: %d} "
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags),
@@ -412,11 +410,11 @@ int ad_close(struct adouble *ad, int adflags)
 
     if ((adflags & ADFLAGS_DF) && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == AD_SYMLINK)) {        
         if (ad->ad_data_refcount)
-            ad->ad_data_refcount--;
+            if (--ad->ad_data_refcount == 0)
+                adf_lock_free(&ad->ad_data_fork);                
         if (--ad->ad_data_fork.adf_refcount == 0) {
             if (ad_data_closefd(ad) < 0)
                 err = -1;
-            adf_lock_free(&ad->ad_data_fork);
         }
     }
 
@@ -427,14 +425,13 @@ int ad_close(struct adouble *ad, int adflags)
             if (close( ad_meta_fileno(ad)) < 0)
                 err = -1;
             ad_meta_fileno(ad) = -1;
-            if (ad->ad_vers == AD_VERSION2)
-                adf_lock_free(ad->ad_mdp);
         }
     }
 
     if (adflags & ADFLAGS_RF) {
         if (ad->ad_reso_refcount)
-            ad->ad_reso_refcount--;
+            if (--ad->ad_reso_refcount == 0)
+                adf_lock_free(ad->ad_rfp);
         if (ad->ad_vers == AD_VERSION_EA) {
             if ((ad_reso_fileno(ad) != -1)
                 && !(--ad->ad_rfp->adf_refcount)) {
@@ -442,12 +439,11 @@ int ad_close(struct adouble *ad, int adflags)
                     err = -1;
                 ad->ad_rlen = 0;
                 ad_reso_fileno(ad) = -1;
-                adf_lock_free(ad->ad_rfp);
             }
         }
     }
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_close(%s): END: %d {d: %d, m: %d, r: %d} "
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags), err,
index 92f0ab89224501331c5a010dd323196ced0258b3..173adc4c1f113f22b9bdeeee05d000f993b4cdee 100644 (file)
@@ -64,7 +64,7 @@ static int set_lock(int fd, int cmd,  struct flock *lock)
 {
     EC_INIT;
 
-    LOG(log_debug, logtype_default, "set_lock(fd: %d, %s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "set_lock(fd: %d, %s, %s, off: %jd (%s), len: %jd): BEGIN",
         fd, cmd == F_SETLK ? "F_SETLK" : "F_GETLK",
         lock->l_type == F_RDLCK ? "F_RDLCK" : lock->l_type == F_WRLCK ? "F_WRLCK" : "F_UNLCK",
         (intmax_t)lock->l_start,
@@ -361,7 +361,7 @@ int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len
     int type;  
     int ret = 0, fcntl_lock_err = 0;
 
-    LOG(log_debug, logtype_default, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         locktypetostr(locktype),
         (intmax_t)off,
@@ -496,7 +496,7 @@ exit:
             set_lock(adf->adf_fd, F_SETLK, &lock);
         }
     }
-    LOG(log_debug, logtype_default, "ad_lock: END: %d", ret);
+    LOG(log_debug, logtype_ad, "ad_lock: END: %d", ret);
     return ret;
 }
 
@@ -507,7 +507,7 @@ int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t
     int err;
     int type;  
 
-    LOG(log_debug, logtype_default, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         locktypetostr(locktype),
         (intmax_t)off,
@@ -564,14 +564,14 @@ int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t
         adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
 
 exit:
-    LOG(log_debug, logtype_default, "ad_tmplock: END: %d", err);
+    LOG(log_debug, logtype_ad, "ad_tmplock: END: %d", err);
     return err;
 }
 
 /* --------------------- */
 void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
 {
-    LOG(log_debug, logtype_default, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
+    LOG(log_debug, logtype_ad, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
 
     if (ad_data_fileno(ad) != -1) {
         adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl);
@@ -580,7 +580,7 @@ void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
         adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl);
     }
 
-    LOG(log_debug, logtype_default, "ad_unlock: END");
+    LOG(log_debug, logtype_ad, "ad_unlock: END");
 }
 
 /*!
@@ -598,7 +598,7 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
     int ret = 0;
     off_t lock_offset;
 
-    LOG(log_debug, logtype_default, "ad_testlock(%s, off: %jd (%s): BEGIN",
+    LOG(log_debug, logtype_ad, "ad_testlock(%s, off: %jd (%s): BEGIN",
         eid == ADEID_DFORK ? "data" : "reso",
         (intmax_t)off,
         shmdstrfromoff(off));
@@ -611,7 +611,7 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
 
     ret = testlock(&ad->ad_data_fork, lock_offset, 1);
 
-    LOG(log_debug, logtype_default, "ad_testlock: END: %d", ret);
+    LOG(log_debug, logtype_ad, "ad_testlock: END: %d", ret);
     return ret;
 }
 
@@ -630,7 +630,6 @@ int ad_testlock(struct adouble *ad, int eid, const off_t off)
 uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
 {
     uint16_t ret = 0;
-    struct ad_fd *adf;
     off_t off;
     off_t len;
 
index 3e06e93fb6cfe7e715ac0de66be0d58cf0ae72a7..6915cbb189aa3618680d3a1abcb3f5d39132c745 100644 (file)
@@ -108,10 +108,12 @@ static int ad_mkrf(const char *path);
 static int ad_header_read(const char *path, struct adouble *ad, const struct stat *hst);
 static int ad_header_upgrade(struct adouble *ad, const char *name);
 
+#ifdef HAVE_EAFD
 static int ad_mkrf_ea(const char *path);
+#endif
 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst);
 static int ad_header_upgrade_ea(struct adouble *ad, const char *name);
-static int ad_reso_size(const char *path, int adflags, struct adouble *ad);
+off_t ad_reso_size(const char *path, int adflags, struct adouble *ad);
 static int ad_mkrf_osx(const char *path);
 
 
@@ -316,10 +318,10 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     uint16_t            ashort;
     struct stat         st;
 
-    LOG(log_debug, logtype_default, "new_ad_header(\"%s\")", path);
+    LOG(log_debug, logtype_ad, "new_ad_header(\"%s\")", path);
 
     if (ad->ad_magic == AD_MAGIC) {
-        LOG(log_debug, logtype_default, "new_ad_header(\"%s\"): already initialized", path);
+        LOG(log_debug, logtype_ad, "new_ad_header(\"%s\"): already initialized", path);
         return 0;
     }
 
@@ -344,22 +346,9 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
         eid++;
     }
 
-    /* put something sane in the directory finderinfo */
-    if (stp == NULL) {
-        stp = &st;
-        if (lstat(path, &st) != 0)
-            return -1;
-    }
-
-    if ((adflags & ADFLAGS_DIR)) {
-        /* set default view */
-        ashort = htons(FINDERINFO_CLOSEDVIEW);
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
-    } else {
-        /* set default creator/type fields */
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
-        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
-    }
+    /* set default creator/type fields */
+    memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
+    memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
 
     /* make things invisible */
     if ((ad->ad_options & ADVOL_INVDOTS)
@@ -373,6 +362,11 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     }
 
     /* put something sane in the date fields */
+    if (stp == NULL) {
+        stp = &st;
+        if (lstat(path, &st) != 0)
+            return -1;
+    }
     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
@@ -409,7 +403,7 @@ static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
             ad->ad_eid[ eid ].ade_len = len;
         } else if (!warning) {
             warning = 1;
-            LOG(log_warning, logtype_default, "parse_entries: bogus eid: %d", eid);
+            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %d", eid);
         }
     }
 }
@@ -444,7 +438,7 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
     ad->ad_version = ntohl( ad->ad_version );
 
     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
+        LOG(log_error, logtype_ad, "ad_open: can't parse AppleDouble header.");
         errno = EIO;
         return -1;
     }
@@ -461,7 +455,7 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
 
     buf += AD_HEADER_LEN;
     if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
+        LOG(log_error, logtype_ad, "ad_header_read: can't read entry info.");
         errno = EIO;
         return -1;
     }
@@ -473,13 +467,13 @@ static int ad_header_read(const char *path _U_, struct adouble *ad, const struct
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
         ) {
-        LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset.");
+        LOG(log_error, logtype_ad, "ad_header_read: problem with rfork entry offset.");
         errno = EIO;
         return -1;
     }
 
     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
-        LOG(log_error, logtype_default, "ad_header_read: can't read in entries.");
+        LOG(log_error, logtype_ad, "ad_header_read: can't read in entries.");
         errno = EIO;
         return -1;
     }
@@ -505,7 +499,7 @@ int ad_valid_header_osx(const char *path)
     char                *buf = &adosx.ad_data[0];
     ssize_t             header_len;
 
-    LOG(log_debug, logtype_afpd, "ad_valid_header_osx(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): BEGIN", fullpathname(path));
 
     EC_NEG1( fd = open(path, O_RDONLY) );
 
@@ -521,12 +515,20 @@ int ad_valid_header_osx(const char *path)
     adosx.ad_version = ntohl(adosx.ad_version);
 
     if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_afpd, "ad_valid_header_osx: not an adouble:ox file");
+        LOG(log_warning, logtype_ad, "ad_valid_header_osx(\"%s\"): not an adouble:osx file", fullpathname(path));
         EC_FAIL;
     }
 
+    if (strncmp(buf + ADEDOFF_FILLER, "Mac OS X", strlen("Mac OS X")) == 0)
+        /*
+         * It's a split fork created by OS X, it's not our "own" ._ file
+         * and thus not a valid header in this context.
+         * We allow enumeration and access.
+         */
+        EC_FAIL;
+
 EC_CLEANUP:
-    LOG(log_debug, logtype_afpd, "ad_valid_header_osx(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad, "ad_valid_header_osx(\"%s\"): END: %d", fullpathname(path), ret);
     if (fd != -1)
         close(fd);
     if (ret != 0)
@@ -561,7 +563,7 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
     adosx.ad_version = ntohl(adosx.ad_version);
 
     if ((adosx.ad_magic != AD_MAGIC) || (adosx.ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: can't parse AppleDouble header");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: can't parse AppleDouble header");
         errno = EIO;
         return -1;
     }
@@ -575,7 +577,7 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
 
     buf += AD_HEADER_LEN;
     if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: can't read entry info.");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: can't read entry info.");
         errno = EIO;
         return -1;
     }
@@ -587,7 +589,7 @@ static int ad_header_read_osx(const char *path _U_, struct adouble *ad, const st
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
         || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
         ) {
-        LOG(log_error, logtype_afpd, "ad_header_read_osx: problem with rfork entry offset.");
+        LOG(log_error, logtype_ad, "ad_header_read_osx: problem with rfork entry offset.");
         errno = EIO;
         return -1;
     }
@@ -606,6 +608,7 @@ EC_CLEANUP:
 
 static int ad_header_read_ea(const char *path, struct adouble *ad, const struct stat *hst _U_)
 {
+    EC_INIT;
     uint16_t nentries;
     int      len;
     ssize_t  header_len;
@@ -614,16 +617,16 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     if (ad_meta_fileno(ad) != -1)
         header_len = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA);
     else
-        header_len = sys_lgetxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
-     if (header_len < 1) {
-        LOG(log_debug, logtype_default, "ad_header_read_ea: %s", strerror(errno));
-        return -1;
+        header_len = sys_getxattr(path, AD_EA_META, ad->ad_data, AD_DATASZ_EA);
+    if (header_len < 1) {
+        LOG(log_debug, logtype_ad, "ad_header_read_ea: %s", strerror(errno));
+        EC_FAIL;
     }
 
-    if (header_len < AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: bogus AppleDouble header.");
-        errno = EIO;
-        return -1;
+    if (header_len < AD_DATASZ_EA) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): short metadata EA", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
     }
 
     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
@@ -633,29 +636,45 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     ad->ad_version = ntohl( ad->ad_version );
 
     if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: wrong magic or version");
-        errno = EIO;
-        return -1;
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): wrong magic or version", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
     }
 
     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
     nentries = ntohs( nentries );
-
-    /* Protect against bogus nentries */
-    len = nentries * AD_ENTRY_LEN;
-    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
-        len = sizeof(ad->ad_data) - AD_HEADER_LEN;
-    if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_default, "ad_header_read_ea: can't read entry info.");
-        errno = EIO;
-        return -1;
+    if (nentries != ADEID_NUM_EA) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid number of entries: %d", fullpathname(path), nentries);
+        errno = EINVAL;
+        EC_FAIL;
     }
-    nentries = len / AD_ENTRY_LEN;
 
     /* Now parse entries */
     parse_entries(ad, buf + AD_HEADER_LEN, nentries);
 
-    return 0;
+    if (nentries != ADEID_NUM_EA
+        || !ad_entry(ad, ADEID_FINDERI)
+        || !ad_entry(ad, ADEID_COMMENT)
+        || !ad_entry(ad, ADEID_FILEDATESI)
+        || !ad_entry(ad, ADEID_AFPFILEI)
+        || !ad_entry(ad, ADEID_PRIVDEV)
+        || !ad_entry(ad, ADEID_PRIVINO)
+        || !ad_entry(ad, ADEID_PRIVSYN)
+        || !ad_entry(ad, ADEID_PRIVID)) {
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): invalid metadata EA", fullpathname(path));
+        errno = EINVAL;
+        EC_FAIL;
+    }
+
+EC_CLEANUP:
+    if (ret != 0 && errno == EINVAL) {
+        become_root();
+        (void)sys_removexattr(path, AD_EA_META);
+        unbecome_root();
+        LOG(log_error, logtype_ad, "ad_header_read_ea(\"%s\"): deleted invalid metadata EA", fullpathname(path), nentries);
+        errno = ENOENT;
+    }
+    EC_EXIT;
 }
 
 /*!
@@ -683,11 +702,13 @@ static int ad_mkrf(const char *path)
     return 0;
 }
 
+#ifdef HAVE_EAFD
 static int ad_mkrf_ea(const char *path _U_)
 {
     AFP_PANIC("ad_mkrf_ea: dont use");
     return 0;
 }
+#endif
 
 static int ad_mkrf_osx(const char *path _U_)
 {
@@ -754,7 +775,8 @@ static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
  *
  * We're called because opening ADFLAGS_HF caused an error.
  * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
- * 2. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
+ * 2. Open non-existent ressource fork, this will just result in first read return EOF
+ * 3. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
  *    ought to close it before returning with an error condition.
  */
 static int ad_error(struct adouble *ad, int adflags)
@@ -763,7 +785,9 @@ static int ad_error(struct adouble *ad, int adflags)
     if (adflags & ADFLAGS_NOHF) { /* 1 */
         return 0;
     }
-    if (adflags & (ADFLAGS_DF | ADFLAGS_SETSHRMD | ADFLAGS_CHECK_OF)) { /* 2 */
+    if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_RF) && (errno == ENOENT)) /* 2 */
+        return 0;
+    if (adflags & (ADFLAGS_DF | ADFLAGS_SETSHRMD | ADFLAGS_CHECK_OF)) { /* 3 */
         ad_close( ad, ADFLAGS_DF );
         err = errno;
     }
@@ -800,6 +824,9 @@ static int ad2openflags(const struct adouble *ad, int adfile, int adflags)
     if (adflags & ADFLAGS_TRUNC)
         oflags |= O_TRUNC;
 
+    if (!(ad->ad_options & ADVOL_FOLLO_SYML))
+        oflags |= O_NOFOLLOW;
+
     return oflags;
 }
 
@@ -812,7 +839,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
     int         st_invalid = -1;
     ssize_t     lsz;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_df(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -833,7 +860,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
         goto EC_CLEANUP;
     }
 
-    oflags = O_NOFOLLOW | ad2openflags(ad, ADFLAGS_DF, adflags);
+    oflags = ad2openflags(ad, ADFLAGS_DF, adflags);
 
     admode = mode;
     if ((adflags & ADFLAGS_CREATE)) {
@@ -878,7 +905,7 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble
     ad->ad_data_fork.adf_refcount++;
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_df(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -898,7 +925,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
     mode_t      admode;
     int         st_invalid = -1;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_v2(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -921,8 +948,8 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
     }
 
     ad_p = ad->ad_ops->ad_path(path, adflags);
-    oflags = O_NOFOLLOW | ad2openflags(ad, ADFLAGS_HF, adflags);
-    LOG(log_debug, logtype_default,"ad_open_hf_v2(\"%s\"): open flags: %s",
+    oflags = ad2openflags(ad, ADFLAGS_HF, adflags);
+    LOG(log_debug, logtype_ad,"ad_open_hf_v2(\"%s\"): open flags: %s",
         fullpathname(path), openflags2logstr(oflags));
     nocreatflags = oflags & ~(O_CREAT | O_EXCL);
 
@@ -949,7 +976,7 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou
             /*
              * We're expecting to create a new adouble header file here
              */
-            LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
+            LOG(log_debug, logtype_ad, "ad_open(\"%s\"): creating adouble file",
                 fullpathname(path));
             admode = mode;
             errno = 0;
@@ -1010,7 +1037,7 @@ EC_CLEANUP:
         ad_meta_fileno(ad) = -1;
         ad->ad_mdp->adf_refcount = 0;
     }
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_v2(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -1022,18 +1049,17 @@ EC_CLEANUP:
 static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
 {
     EC_INIT;
-    ssize_t rforklen;
     int oflags;
     int opened = 0;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_ea(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
 
-    oflags = O_NOFOLLOW | (ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC));
+    oflags = ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC);
 
     if (ad_meta_fileno(ad) == AD_SYMLINK)
         goto EC_CLEANUP;
@@ -1043,7 +1069,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
         if ((oflags & O_RDWR) &&
             /* and it was already denied: */
             (ad->ad_mdp->adf_flags & O_RDONLY)) {
-            LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s",
+            LOG(log_error, logtype_ad, "ad_open_hf_ea(%s): rw request for ro file: %s",
                 fullpathname(path), strerror(errno));
             errno = EACCES;
             EC_FAIL;
@@ -1056,7 +1082,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
             if (adflags & ADFLAGS_DIR)
                 /* For directories we open the directory RDONYL so we can later fchdir()  */
                 oflags = (oflags & ~O_RDWR) | O_RDONLY;
-            LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
+            LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): opening base file for meta adouble EA", path);
             EC_NEG1(ad_meta_fileno(ad) = open(path, oflags));
             opened = 1;
             ad->ad_mdp->adf_flags = oflags;
@@ -1066,16 +1092,20 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
     /* Read the adouble header in and parse it.*/
     if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) {
         if (!(adflags & ADFLAGS_CREATE)) {
-            LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): can't read metadata EA", path);
+            LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): can't read metadata EA", path);
             errno = ENOENT;
             EC_FAIL;
         }
+        if ((adflags & ADFLAGS_CREATE) && (ad->ad_options & ADVOL_RO)) {
+            errno = EROFS;
+            EC_FAIL;
+        }
 
-        LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
+        LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
 
         /* It doesnt exist, EPERM or another error */
         if (!(errno == ENOATTR || errno == ENOENT)) {
-            LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
+            LOG(log_error, logtype_ad, "ad_open_hf_ea: unexpected: %s", strerror(errno));
             EC_FAIL;
         }
 
@@ -1083,12 +1113,12 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
         EC_NEG1_LOG(new_ad_header(ad, path, NULL, adflags));
         ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
         ad_flush(ad);
-        LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
+        LOG(log_debug, logtype_ad, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
     }
 
     if (ad_meta_fileno(ad) != -1)
         ad->ad_mdp->adf_refcount++;
-    (void)ad_reso_size(path, adflags, ad);
+    ad->ad_rlen = ad_reso_size(path, adflags, ad);
 
 EC_CLEANUP:
     if (ret != 0 && opened && ad_meta_fileno(ad) != -1) {
@@ -1096,7 +1126,7 @@ EC_CLEANUP:
         ad_meta_fileno(ad) = -1;
         ad->ad_mdp->adf_refcount = 0;
     }
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open_hf_ea(\"%s\", %s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags), ret,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
@@ -1110,8 +1140,7 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
 {
     int ret = 0;
 
-    memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
-    ad->ad_rlen = 0;
+    ad->ad_meta_refcount++;
 
     switch (ad->ad_vers) {
     case AD_VERSION2:
@@ -1125,66 +1154,77 @@ static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *a
         break;
     }
 
-    if (ret != 0)
+    if (ret != 0) {
+        ad->ad_meta_refcount--;
         ret = ad_error(ad, adflags);
+    }
 
     return ret;
 }
 
 /*!
- * Get resofork length for adouble:ea
+ * Get resofork length for adouble:ea, parameter 'ad' may be NULL
  */
-static int ad_reso_size(const char *path, int adflags, struct adouble *ad)
+off_t ad_reso_size(const char *path, int adflags, struct adouble *ad)
 {
     EC_INIT;
     struct stat st;
+    off_t rlen;
 
-    if (adflags & ADFLAGS_DIR) {
-        ad->ad_rlen = 0;
-        goto EC_CLEANUP;
-    }
+    if (adflags & ADFLAGS_DIR)
+        EC_FAIL;
 
-    LOG(log_debug, logtype_default, "ad_reso_size(\"%s\"): BEGIN", path);
+    LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): BEGIN", path);
 
 #ifdef HAVE_EAFD
     ssize_t easz;
 
-    if (ad_reso_fileno(ad) != -1) {
+    if (ad && ad_reso_fileno(ad) != -1) {
         EC_NEG1( fstat(ad_reso_fileno(ad), &st) );
-        ad->ad_rlen = st.st_size;
-    } else if (ad_meta_fileno(ad) != -1) {
-        EC_NEG1( (ad->ad_rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) );
+        rlen = st.st_size;
+    } else if (ad && ad_meta_fileno(ad) != -1) {
+        EC_NEG1( (rlen = sys_fgetxattr(ad_meta_fileno(ad), AD_EA_RESO, NULL, 0)) );
     } else {
-        EC_NEG1( (ad->ad_rlen = sys_lgetxattr(path, AD_EA_RESO, NULL, 0)) );
+        EC_NEG1( (rlen = sys_lgetxattr(path, AD_EA_RESO, NULL, 0)) );
     }
 
 #else
     const char *rfpath;
-    EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags));
+    EC_NULL_LOG( rfpath = ad_path_osx(path, adflags));
     EC_ZERO( lstat(rfpath, &st));
     if (st.st_size > ADEDOFF_RFORK_OSX)
-        ad->ad_rlen = st.st_size - ADEDOFF_RFORK_OSX;
+        rlen = st.st_size - ADEDOFF_RFORK_OSX;
     else
-        ad->ad_rlen = 0;
+        rlen = 0;
 #endif
 
-    LOG(log_debug, logtype_default, "ad_reso_size(\"%s\"): size: %zd", path, ad->ad_rlen);
+    LOG(log_debug, logtype_ad, "ad_reso_size(\"%s\"): size: %zd", path, rlen);
 
 EC_CLEANUP:
     if (ret != 0)
-        ad->ad_rlen = 0;
-    EC_EXIT;
+        rlen = 0;
+    return rlen;
 }
 
 static int ad_open_rf_v2(const char *path, int adflags, int mode, struct adouble *ad)
 {
+    EC_INIT;
+
     /*
      * ad_open_hf_v2() does the work, but if it failed and adflags are ADFLAGS_NOHF | ADFLAGS_RF
      * ad_open_hf_v2() didn't give an error, but we're supposed to return a reso fork fd
      */
-    if (!AD_RSRC_OPEN(ad) && !(adflags & ADFLAGS_NORF))
-        return -1;
-    return 0;
+
+    LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): BEGIN", fullpathname(path));
+
+    if (!AD_META_OPEN(ad) && !(adflags & (ADFLAGS_NORF | ADFLAGS_RDONLY)))
+        EC_FAIL;
+    if (AD_META_OPEN(ad))
+        ad->ad_reso_refcount++;
+
+EC_CLEANUP:
+    LOG(log_debug, logtype_ad, "ad_open_rf_v2(\"%s\"): END: %d", fullpathname(path), ret);
+    EC_EXIT;
 }
 
 static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble *ad)
@@ -1193,15 +1233,14 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
     int oflags;
     int opened = 0;
     int closeflags = adflags & (ADFLAGS_DF | ADFLAGS_HF);
-    ssize_t rlen;
 #ifndef HAVE_EAFD
     const char *rfpath;
     struct stat st;
 #endif
 
-    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
+    LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): BEGIN", fullpathname(path));
 
-    oflags = O_NOFOLLOW | (ad2openflags(ad, ADFLAGS_RF, adflags) & ~O_CREAT);
+    oflags = ad2openflags(ad, ADFLAGS_RF, adflags) & ~O_CREAT;
 
     if (ad_reso_fileno(ad) != -1) {
         /* the file is already open, but we want write access: */
@@ -1212,8 +1251,9 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
             EC_FAIL;
         }
         ad->ad_rfp->adf_flags &= ~( O_TRUNC | O_CREAT );
+        ad->ad_reso_refcount++;
         ad->ad_rfp->adf_refcount++;
-        EC_NEG1_LOG( ad_reso_size(path, adflags, ad));
+        EC_NEG1_LOG( ad->ad_rlen = ad_reso_size(path, adflags, ad));
         goto EC_CLEANUP;
     }
 #ifdef HAVE_EAFD
@@ -1221,53 +1261,97 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
         EC_FAIL;
     if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
         if (!(adflags & ADFLAGS_CREATE)) {
-            errno = ENOENT;
-            EC_FAIL;
+            switch (errno) {
+            case EACCES:
+            case EPERM:
+            case EROFS:
+                if (!(adflags & ADFLAGS_RDONLY)) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                    EC_FAIL;
+                }
+                oflags &= ~O_RDWR;
+                oflags |= O_RDONLY;
+                if ((ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad), AD_EA_RESO, oflags)) == -1) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                    EC_FAIL;
+                }
+                break;
+            case ENOENT:
+                EC_EXIT_STATUS(0);
+            default:
+                LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(path), strerror(errno));
+                EC_FAIL;
+            }
+        } else {
+            oflags |= O_CREAT;
+            EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad),
+                                                             AD_EA_RESO, oflags, 0666) );
         }
-        oflags |= O_CREAT;
-        EC_NEG1_LOG( ad_reso_fileno(ad) = sys_getxattrfd(ad_meta_fileno(ad),
-                                                         AD_EA_RESO, oflags, 0666) ); 
     }
 #else
     EC_NULL_LOG( rfpath = ad->ad_ops->ad_path(path, adflags) );
     if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
-        if (!(adflags & ADFLAGS_CREATE))
-            EC_FAIL;
-        oflags |= O_CREAT;
-        EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
-            path, rfpath);
+        if (!(adflags & ADFLAGS_CREATE)) {
+            switch (errno) {
+            case EACCES:
+            case EPERM:
+            case EROFS:
+                if (!(adflags & ADFLAGS_RDONLY)) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                    EC_FAIL;
+                }
+                oflags &= ~O_RDWR;
+                oflags |= O_RDONLY;
+                if ((ad_reso_fileno(ad) = open(rfpath, oflags)) == -1) {
+                    LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                    EC_FAIL;
+                }
+                break;
+            case ENOENT:
+                EC_EXIT_STATUS(0);
+            default:
+                LOG(log_error, logtype_ad, "ad_open_rf_ea(\"%s\"): \"%s\"", fullpathname(rfpath), strerror(errno));
+                EC_FAIL;
+            }
+        } else {
+            oflags |= O_CREAT;
+            EC_NEG1_LOG( ad_reso_fileno(ad) = open(rfpath, oflags, mode) );
+            LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork: \"%s\"",
+                path, rfpath);
+        }
     }
 #endif
     opened = 1;
     ad->ad_rfp->adf_refcount = 1;
     ad->ad_rfp->adf_flags = oflags;
+    ad->ad_reso_refcount++;
 
 #ifndef HAVE_EAFD
     EC_ZERO_LOG( fstat(ad_reso_fileno(ad), &st) );
     if (ad->ad_rfp->adf_flags & O_CREAT) {
         /* This is a new adouble header file, create it */
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
             path, rfpath);
         EC_NEG1_LOG( new_ad_header(ad, path, NULL, adflags) );
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
             path, rfpath);
         ad_flush(ad);
     } else {
         /* Read the adouble header */
-        LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
+        LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): reading adouble rfork: \"%s\"",
             path, rfpath);
         EC_NEG1_LOG( ad_header_read_osx(NULL, ad, &st) );
     }
 #endif
 
-    (void)ad_reso_size(path, adflags, ad);
+    ad->ad_rlen = ad_reso_size(path, adflags, ad);
 
 EC_CLEANUP:
     if (ret != 0) {
         if (opened && (ad_reso_fileno(ad) != -1)) {
             close(ad_reso_fileno(ad));
             ad_reso_fileno(ad) = -1;
+            ad->ad_reso_refcount--;
             ad->ad_rfp->adf_refcount = 0;
         }
         if (adflags & ADFLAGS_NORF) {
@@ -1280,7 +1364,7 @@ EC_CLEANUP:
         ad->ad_rlen = 0;
     }
 
-    LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
+    LOG(log_debug, logtype_ad, "ad_open_rf(\"%s\"): END: %d", fullpathname(path), ret);
 
     EC_EXIT;
 }
@@ -1478,7 +1562,7 @@ int ad_stat(const char *path, struct stat *stbuf)
     p = ad_dir(path);
     if (!p)
         return -1;
-    return lstat( p, stbuf );
+    return stat( p, stbuf );
 }
 
 /* ----------------
@@ -1500,7 +1584,7 @@ int ad_mkdir( const char *path, mode_t mode)
     int st_invalid;
     struct stat stbuf;
 
-    LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
+    LOG(log_debug, logtype_ad, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
         path, mode, getcwdpath());
 
     st_invalid = ad_mode_st(path, &mode, &stbuf);
@@ -1534,6 +1618,7 @@ static void ad_init_func(struct adouble *ad)
     ad_reso_fileno(ad) = -1;
     ad_meta_fileno(ad) = -1;
     ad->ad_refcount = 1;
+    ad->ad_rlen = 0;
     return;
 }
 
@@ -1603,7 +1688,7 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     va_list args;
     mode_t mode = 0;
 
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open(\"%s\", %s): BEGIN {d: %d, m: %d, r: %d}"
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
@@ -1650,17 +1735,13 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     }
 
     if (adflags & ADFLAGS_HF) {
-        ad->ad_meta_refcount++;
         if (ad_open_hf(path, adflags, mode, ad) != 0) {
-            ad->ad_meta_refcount--;
             EC_FAIL;
         }
     }
 
     if (adflags & ADFLAGS_RF) {
-        ad->ad_reso_refcount++;
         if (ad_open_rf(path, adflags, mode, ad) != 0) {
-            ad->ad_reso_refcount--;
             EC_FAIL;
         }
     }
@@ -1670,7 +1751,7 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     }
 
 EC_CLEANUP:
-    LOG(log_debug, logtype_default,
+    LOG(log_debug, logtype_ad,
         "ad_open(\"%s\"): END: %d {d: %d, m: %d, r: %d}"
         "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), ret,
@@ -1734,7 +1815,7 @@ int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
     if (dirfd != -1) {
 
         if (fchdir(cwdfd) != 0) {
-            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+            LOG(log_error, logtype_ad, "ad_openat: cant chdir back, exiting");
             exit(EXITERR_SYS);
         }
     }
@@ -1801,7 +1882,7 @@ int ad_openat(struct adouble  *ad,
     EC_INIT;
     int cwdfd = -1;
     va_list args;
-    mode_t mode;
+    mode_t mode = 0;
 
     if (dirfd != -1) {
         if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0))
index ed7c0cb4da2b182b93a7c203e615403751682bb0..852a6e963b94f964e3b47d1ea8b67df6cac0717e 100644 (file)
@@ -62,7 +62,6 @@ ssize_t adf_pread(struct ad_fd *ad_fd, void *buf, size_t count, off_t offset)
 ssize_t ad_read( struct adouble *ad, const uint32_t eid, off_t off, char *buf, const size_t buflen)
 {
     ssize_t     cc;
-    ssize_t     rlen;
     off_t r_off = 0;
 
     /* We're either reading the data fork (and thus the data file)
index 337ff6ed3d5f07075ff286dfa58964488c9158f4..6b4ed96d40a9fd09a8173becbce1d28dca3d43f6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: ad_size.c,v 1.8 2010-02-26 14:13:16 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index bfed9b400f27f8fd577310bc4052a76e2e6a5017..5cd3742d69f9890f3fa01a5148fc23fda7ae1e6f 100644 (file)
@@ -52,7 +52,6 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
     EC_INIT;
     struct stat                st;
     ssize_t            cc;
-    size_t roundup;
     off_t    r_off;
 
     if (ad_data_fileno(ad) == AD_SYMLINK) {
@@ -60,7 +59,7 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
         return -1;
     }
 
-    LOG(log_debug, logtype_default, "ad_write: off: %ju, size: %zu, eabuflen: %zu",
+    LOG(log_debug, logtype_ad, "ad_write: off: %ju, size: %zu, eabuflen: %zu",
         (uintmax_t)off, buflen, ad->ad_rlen);
     
     if ( eid == ADEID_DFORK ) {
@@ -94,7 +93,6 @@ ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const cha
         return -1; /* we don't know how to write if it's not a ressource or data fork */
     }
 
-EC_CLEANUP:
     if (ret != 0)
         return ret;
     return( cc );
@@ -159,13 +157,13 @@ char            c = 0;
 }
 
 /* ------------------------ */
-int ad_rtruncate( struct adouble *ad, const off_t size)
+int ad_rtruncate(struct adouble *ad, const char *uname, const off_t size)
 {
     EC_INIT;
 
 #ifndef HAVE_EAFD
     if (ad->ad_vers == AD_VERSION_EA && size == 0)
-        EC_NEG1( unlink(ad->ad_ops->ad_path(ad->ad_name, 0)) );
+        EC_NEG1( unlink(ad->ad_ops->ad_path(uname, 0)) );
     else
 #endif
         EC_NEG1( sys_ftruncate(ad_reso_fileno(ad), size + ad->ad_eid[ ADEID_RFORK ].ade_off) );
@@ -174,15 +172,15 @@ EC_CLEANUP:
     if (ret == 0)
         ad->ad_rlen = size;    
     else
-        LOG(log_error, logtype_default, "ad_rtruncate(\"%s\"): %s",
-            fullpathname(ad->ad_name), strerror(errno));
+        LOG(log_error, logtype_ad, "ad_rtruncate(\"%s\"): %s",
+            fullpathname(uname), strerror(errno));
     EC_EXIT;
 }
 
 int ad_dtruncate(struct adouble *ad, const off_t size)
 {
     if (sys_ftruncate(ad_data_fileno(ad), size) < 0) {
-        LOG(log_error, logtype_default, "sys_ftruncate(fd: %d): %s",
+        LOG(log_error, logtype_ad, "sys_ftruncate(fd: %d): %s",
             ad_data_fileno(ad), strerror(errno));
         return -1;
     }
index 7b59b2e8ee67a9340da551ad81a164a1df544e27..98e1a1553864bf2fc820d080b99ae56ab01b295b 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -77,7 +76,6 @@
  */
 bstring brefcstr (char *str) {
     bstring b;
-    int i;
     size_t j;
 
        if (str == NULL)
@@ -90,7 +88,7 @@ bstring brefcstr (char *str) {
 
        b->slen = (int) j;
     b->mlen = -1;
-    b->data = str;
+    b->data = (unsigned char *)str;
 
        return b;
 }
index fcc01df2eb6ded40fc339f039389e548f6ab97e7..2e11ea81c23a4466288f17b34216d81ecaf67c0d 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_close.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 1d14640af1fb68c77b2e9903a07385a36e47b619..ace358aa8b617b79a1f01fa0f4d8274b7c8e9822 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_delete.c,v 1.4 2009-10-29 13:38:16 didg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
index 9184264ae8ec40f420d7847328fac9050496cb30..f88a9e29c0e103416b9f7f96c21f5bdd70b9447e 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_meta.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
index cf9d905a614cf9bb65293b363084ab6ec14c10cd..e8a4bdd050b2abe559e816d8928d21fa7a2e25f1 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_meta.h,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #define CNID_META_CNID_LEN      4
index 60141c3bea5f7de7c1e688de9a983f00f21ab3b5..ba14bd8e46fa3d6a28f043d016477ba21307ae06 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_nextid.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 056420dad099f1123a6e514f98630055d285fa3a..757d1b0da1e2c3c351ef618679e5ab37b2c64b48 100644 (file)
@@ -144,7 +144,7 @@ static struct _cnid_db *cnid_cdb_new(const char *volpath)
     cdb->cnid_close = cnid_cdb_close;
     cdb->cnid_getstamp = cnid_cdb_getstamp;
     cdb->cnid_rebuild_add = cnid_cdb_rebuild_add;
-
+    cdb->cnid_wipe = NULL;
     return cdb;
 }
 
index be2db1023692be7e7d966b76e23d1773b37ee1a0..4620782e14057f4ce5cf29d7976ad7e6d3c9b885 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_rebuild_add.c,v 1.6 2009-11-20 17:22:11 didg Exp $
  *
  * All Rights Reserved. See COPYRIGHT.
  *
index 6c16ce7ec6a12b6341d5758075ba1325b7f09945..63ed9f8a1c14b51c9de5837ef32e91ab2713d558 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_cdb_resolve.c,v 1.5 2009-10-29 13:38:16 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index f0b8903d6ccf95782bbb7dcd4809d01239733402..021cecfbfa35ac6425ef9afbde9b61273458fe6f 100644 (file)
@@ -338,3 +338,15 @@ cnid_t ret;
     unblock_signal(cdb->flags);
     return ret;
 }
+
+/* --------------- */
+int cnid_wipe(struct _cnid_db *cdb)
+{
+    int ret = 0;
+
+    block_signal(cdb->flags);
+    if (cdb->cnid_wipe)
+        ret = cdb->cnid_wipe(cdb);
+    unblock_signal(cdb->flags);
+    return ret;
+}
index 7cc108d3bcf5013dc6724ff96316034b36aad4c8..75fc5db380f94f7a400a070b8eecde3c4abbdf52 100644 (file)
@@ -1,6 +1,5 @@
 
 /* 
- * $Id: cnid_init.c,v 1.3 2009-10-13 22:55:37 didg Exp $
  *
  * Copyright (c) 2003 the Netatalk Team
  * Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
index c96a22596163659fc87be4a6b3a60f3eafc6e321..ccf01343c13639a963f6423e57a19420179c6a51 100644 (file)
@@ -356,47 +356,18 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
     time_t orig, t;
     int clean = 1; /* no errors so far - to prevent sleep on first try */
 
-    if (db->changed) {
-        /* volume and db don't have the same timestamp
-         */
-        return -1;
-    }
     while (1) {
         if (db->fd == -1) {
-            struct cnid_dbd_rqst rqst_stamp;
-            struct cnid_dbd_rply rply_stamp;
-            char  stamp[ADEDLEN_PRIVSYN];
-
             LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
             if ((db->fd = init_tsock(db)) < 0) {
                 goto transmit_fail;
             }
-            dbd_initstamp(&rqst_stamp);
-            memset(stamp, 0, ADEDLEN_PRIVSYN);
-            rply_stamp.name = stamp;
-            rply_stamp.namelen = ADEDLEN_PRIVSYN;
-
-            if (dbd_rpc(db, &rqst_stamp, &rply_stamp) < 0)
-                goto transmit_fail;
-            if (dbd_reply_stamp(&rply_stamp ) < 0)
-                goto transmit_fail;
-
             if (db->notfirst) {
-                LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd, comparing database stamps...");
-                if (memcmp(stamp, db->stamp, ADEDLEN_PRIVSYN)) {
-                    LOG(log_error, logtype_cnid, "transmit: ... not the same db!");
-                    db->changed = 1;
-                    return -1;
-                }
-                LOG(log_debug7, logtype_cnid, "transmit: ... OK.");
+                LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd");
             } else { /* db->notfirst == 0 */
                 db->notfirst = 1;
-                if (db->client_stamp)
-                    memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
-                memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
             }
-            LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
-                db->db_dir, *(uint64_t *)stamp);
+            LOG(log_debug, logtype_cnid, "transmit: attached to '%s'", db->db_dir);
         }
         if (!dbd_rpc(db, rqst, rply)) {
             LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
@@ -456,7 +427,7 @@ static struct _cnid_db *cnid_dbd_new(const char *volpath)
     cdb->cnid_update = cnid_dbd_update;
     cdb->cnid_rebuild_add = cnid_dbd_rebuild_add;
     cdb->cnid_close = cnid_dbd_close;
-
+    cdb->cnid_wipe = cnid_dbd_wipe;
     return cdb;
 }
 
@@ -531,6 +502,32 @@ void cnid_dbd_close(struct _cnid_db *cdb)
     return;
 }
 
+/**
+ * Get the db stamp
+ **/
+static int cnid_dbd_stamp(CNID_private *db)
+{
+    struct cnid_dbd_rqst rqst_stamp;
+    struct cnid_dbd_rply rply_stamp;
+    char  stamp[ADEDLEN_PRIVSYN];
+
+    dbd_initstamp(&rqst_stamp);
+    memset(stamp, 0, ADEDLEN_PRIVSYN);
+    rply_stamp.name = stamp;
+    rply_stamp.namelen = ADEDLEN_PRIVSYN;
+
+    if (transmit(db, &rqst_stamp, &rply_stamp) < 0)
+        return -1;
+    if (dbd_reply_stamp(&rply_stamp ) < 0)
+        return -1;
+
+    if (db->client_stamp)
+        memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
+    memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
+
+    return 0;
+}
+
 /* ---------------------- */
 cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
                     cnid_t did, const char *name, size_t len, cnid_t hint)
@@ -566,8 +563,8 @@ cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
     rqst.name = name;
     rqst.namelen = len;
 
-    LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
-        ntohl(did), name, (long long)st->st_ino, rqst.type);
+    LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', dev: 0x%llx, inode: 0x%llx, type: %s",
+        ntohl(did), name, (long long)rqst.dev, (long long)st->st_ino, rqst.type ? "dir" : "file");
 
     rply.namelen = 0;
     if (transmit(db, &rqst, &rply) < 0) {
@@ -706,7 +703,9 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le
     return name;
 }
 
-/* ---------------------- */
+/**
+ * Caller passes buffer where we will store the db stamp
+ **/
 int cnid_dbd_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
 {
     CNID_private *db;
@@ -718,8 +717,8 @@ int cnid_dbd_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
     }
     db->client_stamp = buffer;
     db->stamp_size = len;
-    memset(buffer,0, len);
-    return 0;
+
+    return cnid_dbd_stamp(db);
 }
 
 /* ---------------------- */
@@ -994,6 +993,39 @@ int cnid_dbd_delete(struct _cnid_db *cdb, const cnid_t id)
     }
 }
 
+int cnid_dbd_wipe(struct _cnid_db *cdb)
+{
+    CNID_private *db;
+    struct cnid_dbd_rqst rqst;
+    struct cnid_dbd_rply rply;
+
+    if (!cdb || !(db = cdb->_private)) {
+        LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
+        errno = CNID_ERR_PARAM;
+        return -1;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
+
+    RQST_RESET(&rqst);
+    rqst.op = CNID_DBD_OP_WIPE;
+    rqst.cnid = 0;
+
+    rply.namelen = 0;
+    if (transmit(db, &rqst, &rply) < 0) {
+        errno = CNID_ERR_DB;
+        return -1;
+    }
+
+    if (rply.result != CNID_DBD_RES_OK) {
+        errno = CNID_ERR_DB;
+        return -1;
+    }
+    LOG(log_debug, logtype_cnid, "cnid_dbd_wipe: ok");
+
+    return cnid_dbd_stamp(db);
+}
+
 
 struct _cnid_module cnid_dbd_module = {
     "dbd",
index fdba4c042a62591e3e771ed449ad7de6588986ca..69ff5a64fb252a0e8424a76ebb9e688eded6174b 100644 (file)
@@ -32,7 +32,7 @@ extern int    cnid_dbd_update     (struct _cnid_db *, cnid_t, const struct stat
 extern int    cnid_dbd_delete     (struct _cnid_db *, const cnid_t);
 extern cnid_t cnid_dbd_rebuild_add(struct _cnid_db *, const struct stat *,
                                    cnid_t, const char *, size_t, cnid_t);
-
+extern int    cnid_dbd_wipe       (struct _cnid_db *cdb);
 /* FIXME: These functions could be static in cnid_dbd.c */
 
 #endif /* include/atalk/cnid_dbd.h */
index 5903d03fe449da281238818e9501c57e812cf9f1..cc2ae1c7d19e79ec3ef8ae13c14936bc491908b7 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: cnid_last.c,v 1.5 2010-03-31 09:47:32 franklahm Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
@@ -139,7 +138,8 @@ static struct _cnid_db *cnid_last_new(const char *volpath)
     cdb->cnid_resolve = cnid_last_resolve;
     cdb->cnid_update = cnid_last_update;
     cdb->cnid_close = cnid_last_close;
-    
+    cdb->cnid_wipe = NULL;
+
     return cdb;
 }
 
index fcb43fa2f6488d5d725a79ee43cec230571e96a3..3203eda94a9f80de30e180f44bd3744755d65b92 100644 (file)
@@ -93,7 +93,7 @@ static int add_cnid (struct _cnid_tdb_private *db, TDB_DATA *key, TDB_DATA *data
     }
 
     /* did/name database */
-    altkey.dptr = (char *) data->dptr +CNID_DID_OFS;
+    altkey.dptr = data->dptr +CNID_DID_OFS;
     altkey.dsize = data->dsize -CNID_DID_OFS;
     if (tdb_store(db->tdb_didname, altkey, altdata, TDB_REPLACE)) {
         goto abort;
@@ -112,7 +112,7 @@ static cnid_t get_cnid(struct _cnid_tdb_private *db)
     
     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
     memset(&data, 0, sizeof(data));
-    rootinfo_key.dptr = ROOTINFO_KEY;
+    rootinfo_key.dptr = (unsigned char *)ROOTINFO_KEY;
     rootinfo_key.dsize = ROOTINFO_KEYLEN;
     
     tdb_chainlock(db->tdb_didname, rootinfo_key);  
@@ -136,7 +136,7 @@ static cnid_t get_cnid(struct _cnid_tdb_private *db)
     }
     
     memset(&data, 0, sizeof(data));
-    data.dptr = (char *)&hint;
+    data.dptr = (unsigned char *)&hint;
     data.dsize = sizeof(hint);
     if (tdb_store(db->tdb_didname, rootinfo_key, data, TDB_REPLACE)) {
         goto cleanup;
@@ -181,7 +181,7 @@ cnid_t cnid_tdb_add(struct _cnid_db *cdb, const struct stat *st,
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    key.dptr = (char *)&hint;
+    key.dptr = (unsigned char *)&hint;
     key.dsize = sizeof(cnid_t);
     if ((data.dptr = make_tdb_data(cdb->flags, lstp, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "tdb_add: Path name is too long");
index cbe89a6e8a8dcd415b3f63fafb198c808057c419..c500181611549fb49deeddfabe646c30ec6aa6c8 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_close.c,v 1.3 2009-11-21 13:38:11 didg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index aa801e584ac29c43a16be416e2a881771fb52d2b..a16a99519327bcab826589b3995524b44a67b2d0 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_delete.c,v 1.4 2009-11-20 19:25:05 didg Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
@@ -26,7 +25,7 @@ int cnid_tdb_delete(struct _cnid_db *cdb, const cnid_t id)
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
 
-    key.dptr  = (char *)&id;
+    key.dptr  = (unsigned char *)&id;
     key.dsize = sizeof(cnid_t);
     data = tdb_fetch(db->tdb_cnid, key);
     if (!data.dptr)
@@ -40,7 +39,7 @@ int cnid_tdb_delete(struct _cnid_db *cdb, const cnid_t id)
     key.dsize = CNID_DEVINO_LEN;
     tdb_delete(db->tdb_devino, key); 
 
-    key.dptr = (char *)data.dptr +CNID_DID_OFS;
+    key.dptr = data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize -CNID_DID_OFS;
     tdb_delete(db->tdb_didname, key); 
 
index 8321d04e868eb20469c061bd08c0057c7dacc7ae..4e7f638dd18debb62470bc4f19b7bcb58be589e7 100644 (file)
@@ -26,7 +26,7 @@ cnid_t cnid_tdb_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t l
     buf += sizeof(did);
     memcpy(buf, name, len);
     *(buf + len) = '\0'; /* Make it a C-string. */
-    key.dptr = start;
+    key.dptr = (unsigned char *)start;
     key.dsize = CNID_DID_LEN + len + 1;
     data = tdb_fetch(db->tdb_didname, key);
     if (!data.dptr)
index f095cd54b9c989e186e66360177b0c4bc4f8110a..c95631afee52574c292cb0534b85b6a407cc6e0f 100644 (file)
@@ -25,7 +25,7 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
         return 0;
     }
 
-    if ((buf = make_tdb_data(cdb->flags, st, did, name, len)) == NULL) {
+    if ((buf = (char *)make_tdb_data(cdb->flags, st, did, name, len)) == NULL) {
         LOG(log_error, logtype_default, "tdb_lookup: Pathname is too long");
         return 0;
     }
@@ -42,7 +42,7 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN);
     memcpy(ino, buf + CNID_INO_OFS, CNID_INO_LEN);
 
-    key.dptr = buf +CNID_DEVINO_OFS;
+    key.dptr = (unsigned char *)buf + CNID_DEVINO_OFS;
     key.dsize  = CNID_DEVINO_LEN;
     cniddata = tdb_fetch(db->tdb_devino, key);
     if (!cniddata.dptr) {
@@ -66,7 +66,7 @@ cnid_t cnid_tdb_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
     }
 
     /* did/name now */
-    key.dptr = buf + CNID_DID_OFS;
+    key.dptr = (unsigned char *)buf + CNID_DID_OFS;
     key.dsize = CNID_DID_LEN + len + 1;
     cniddata = tdb_fetch(db->tdb_didname, key);
     if (!cniddata.dptr) {
index 8aefa42b2278c6f34696d095451ff7e375a2b62e..8e7b12616f0a9f3020e03f743bef034fdd6ce543 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_nextid.c,v 1.2 2005-04-28 20:50:02 bfernhomberg Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index d02ef0bcc054caa1da65ab3ccfa5f6c5885a8b5a..0aee5b4df5469ddbb3282c8d23d67f35f3bb3837 100644 (file)
@@ -56,7 +56,8 @@ static struct _cnid_db *cnid_tdb_new(const char *volpath)
     cdb->cnid_resolve = cnid_tdb_resolve;
     cdb->cnid_update = cnid_tdb_update;
     cdb->cnid_close = cnid_tdb_close;
-    
+    cdb->cnid_wipe = NULL;
+
     return cdb;
 }
 
@@ -124,14 +125,14 @@ struct _cnid_db *cnid_tdb_open(struct cnid_open_args *args)
      * to change the format in any way. */
     memset(&key, 0, sizeof(key));
     memset(&data, 0, sizeof(data));
-    key.dptr = DBVERSION_KEY;
+    key.dptr = (unsigned char *)DBVERSION_KEY;
     key.dsize = DBVERSION_KEYLEN;
 
     data = tdb_fetch(db->tdb_didname, key);
     if (!data.dptr) {
         uint32_t version = htonl(DBVERSION);
 
-        data.dptr = (char *)&version;
+        data.dptr = (unsigned char *)&version;
         data.dsize = sizeof(version);
         if (tdb_store(db->tdb_didname, key, data, TDB_REPLACE)) {
             LOG(log_error, logtype_default, "tdb_open: Error putting new version");
index 5e8312222ca377b7e8e25bcf27d53b9017e6afb3..851f9e55e7be86811bb4106018411f0cc98b5a30 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: cnid_tdb_resolve.c,v 1.4 2009-11-22 14:14:05 franklahm Exp $
  */
 
 #ifdef HAVE_CONFIG_H
@@ -19,7 +18,7 @@ char *cnid_tdb_resolve(struct _cnid_db *cdb, cnid_t * id, void *buffer, size_t l
     if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
         return NULL;
     }
-    key.dptr  = (char *)id;
+    key.dptr  = (unsigned char *)id;
     key.dsize = sizeof(cnid_t);
     data = tdb_fetch(db->tdb_cnid, key);
     if (data.dptr) 
index d367334e06126c8f903fe8bea20d1a0d7efea0b1..6d23b109f528ab71aad7de553339d534fd85431e 100644 (file)
@@ -38,7 +38,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
         free(altdata.dptr);
 
         if (data.dptr) {
-            key.dptr = (char *)data.dptr +CNID_DID_OFS;
+            key.dptr = (unsigned char *)data.dptr +CNID_DID_OFS;
             key.dsize = data.dsize - CNID_DID_OFS;
             tdb_delete(db->tdb_didname, key); 
         
@@ -49,7 +49,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     /* search by did/name */
     data.dptr = make_tdb_data(cdb->flags, st, did, name, len);
     data.dsize = CNID_HEADER_LEN + len + 1;
-    key.dptr = (char *)data.dptr +CNID_DID_OFS;
+    key.dptr = (unsigned char *)data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize - CNID_DID_OFS;
     altdata = tdb_fetch(db->tdb_didname, key);
     if (altdata.dptr) {
@@ -76,7 +76,7 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     memcpy(data.dptr, &id, sizeof(id));
 
     /* Update the old CNID with the new info. */
-    key.dptr = (char *) &id;
+    key.dptr = (unsigned char *) &id;
     key.dsize = sizeof(id);
     if (tdb_store(db->tdb_cnid, key, data, TDB_REPLACE)) {
         goto update_err;
@@ -85,13 +85,13 @@ int cnid_tdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
     /* Put in a new dev/ino mapping. */
     key.dptr = data.dptr +CNID_DEVINO_OFS;
     key.dsize = CNID_DEVINO_LEN;
-    altdata.dptr  = (char *) &id;
+    altdata.dptr  = (unsigned char *) &id;
     altdata.dsize = sizeof(id);
     if (tdb_store(db->tdb_devino, key, altdata, TDB_REPLACE)) {
         goto update_err;
     }
     /* put in a new did/name mapping. */
-    key.dptr = (char *) data.dptr +CNID_DID_OFS;
+    key.dptr = (unsigned char *) data.dptr +CNID_DID_OFS;
     key.dsize = data.dsize -CNID_DID_OFS;
 
     if (tdb_store(db->tdb_didname, key, altdata, TDB_REPLACE)) {
index 2716bb8f955ca79c002dde8237f8107f4141f7e5..481978b9f42f67f8f5897623c9bd62ba66810196 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: getusershell.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * Copyright (c) 1985 Regents of the University of California.
  * All rights reserved.
index ddbfe2d5f88a338e4722db6cc5b42a79a7a8353e..8faae19336149d309234b182002bdead6c3d9cdd 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: mktemp.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * Copyright (c) 1987 Regents of the University of California.
  * All rights reserved.
index 03dd3bb601f1d6cb849d43736c5017aa461a649d..eb73c9754e88c4a5c4c29778c8ddb32c1304f28f 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: rquota_xdr.c,v 1.4 2003-02-17 01:51:08 srittau Exp $
  *
  * taken from the quota-1.55 used on linux. here's the bsd copyright:
  *
index 5a60c24d5c25d30d223b820b3edf496c89009740..80757712192a8240421d71e8f10cce7588310d1a 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_attn.c,v 1.8 2009-10-25 06:13:11 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 80bee3fe702a79cfbd18bd7a7ac4bd8539295aaf..892b982eade06df3553c83ab2d7b00a876f1617b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_close.c,v 1.4 2003-03-12 15:07:06 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 001279cd74ae6ad717eef7fe01a7880a77b3dfab..38477be99e1ac18b9f7e22a22f8cc28500c4fe83 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_getsess.c,v 1.7 2005-04-28 20:50:02 bfernhomberg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  * @param childp    (w) after fork: parent return pointer to child, child returns NULL
  * @returns             0 on sucess, any other value denotes failure
  */
-int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_child_t **childp)
+int dsi_getsession(DSI *dsi, server_child_t *serv_children, int tickleval, afp_child_t **childp)
 {
   pid_t pid;
-  unsigned int ipc_fds[2];  
+  int ipc_fds[2];  
   afp_child_t *child;
 
   if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
@@ -62,7 +61,7 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
     /* using SIGKILL is hokey, but the child might not have
      * re-established its signal handler for SIGTERM yet. */
     close(ipc_fds[1]);
-    if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds[0])) ==  NULL) {
+    if ((child = server_child_add(serv_children, pid, ipc_fds[0])) ==  NULL) {
       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
       close(ipc_fds[0]);
       dsi->header.dsi_flags = DSIFL_REPLY;
@@ -78,7 +77,7 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
   
   /* child: check number of open connections. this is one off the
    * actual count. */
-  if ((serv_children->count >= serv_children->nsessions) &&
+  if ((serv_children->servch_count >= serv_children->servch_nsessions) &&
       (dsi->header.dsi_command == DSIFUNC_OPEN)) {
     LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
     dsi->header.dsi_flags = DSIFL_REPLY;
@@ -121,7 +120,6 @@ int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_chi
     /* set up the tickle timer */
     dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval;
     dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
-    signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
     dsi_opensession(dsi);
     *childp = NULL;
     return 0;
index aec872ab0c15f1aaea351ddeb9febe44860e9f9a..dac4f76b3760efa38c650d5e7b21dc7ba42d71d6 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_getstat.c,v 1.4 2005-09-07 15:27:29 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 711a037b5098925c59c77198bb6350d390d9b5a3..94890dd8014341bbe6ccb02393f35a5456c9775c 100644 (file)
@@ -115,7 +115,7 @@ static int dsi_peek(DSI *dsi)
         if (FD_ISSET(dsi->socket, &readfds)) {
             len = dsi->end - dsi->eof; /* it's ensured above that there's space */
 
-            if ((len = read(dsi->socket, dsi->eof, len)) <= 0) {
+            if ((len = recv(dsi->socket, dsi->eof, len, 0)) <= 0) {
                 if (len == 0) {
                     LOG(log_error, logtype_dsi, "dsi_peek: EOF");
                     return -1;
@@ -185,7 +185,7 @@ static ssize_t buf_read(DSI *dsi, uint8_t *buf, size_t count)
     if (len)
         return len;             /* 2. */
   
-    len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
+    len = readt(dsi->socket, buf, count, 0, 0); /* 3. */
 
     LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
 
@@ -213,7 +213,7 @@ static size_t dsi_buffered_stream_read(DSI *dsi, uint8_t *data, const size_t len
   buflen = MIN(8192, dsi->end - dsi->eof);
   if (buflen > 0) {
       ssize_t ret;
-      ret = read(dsi->socket, dsi->eof, buflen);
+      ret = recv(dsi->socket, dsi->eof, buflen, 0);
       if (ret > 0)
           dsi->eof += ret;
   }
@@ -344,6 +344,16 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
     int sfvcnt;
     struct sendfilevec vec[2];
     ssize_t nwritten;
+#elif defined(FREEBSD)
+    ssize_t nwritten;
+    void *hdrp;
+    struct sf_hdtr hdr;
+    struct iovec iovec;
+    hdr.headers = &iovec;
+    hdr.hdr_cnt = 1;
+    hdr.trailers = NULL;
+    hdr.trl_cnt = 0;
+    hdrp = &hdr;
 #endif
 
     LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length);
@@ -372,6 +382,9 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
     vec[1].sfv_flag = 0;
     vec[1].sfv_off = offset;
     vec[1].sfv_len = length;
+#elif defined(FREEBSD)
+    iovec.iov_base = block;
+    iovec.iov_len = DSI_BLOCKSIZ;
 #else
     dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE);
 #endif
@@ -380,6 +393,10 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
 #ifdef HAVE_SENDFILEV
         nwritten = 0;
         len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten);
+#elif defined(FREEBSD)
+        len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0);
+        if (len == 0)
+            len = nwritten;
 #else
         len = sys_sendfile(dsi->socket, fromfd, &pos, total - written);
 #endif
@@ -388,16 +405,14 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
             case EINTR:
             case EAGAIN:
                 len = 0;
-#ifdef HAVE_SENDFILEV
+#if defined(HAVE_SENDFILEV) || defined(FREEBSD)
                 len = (size_t)nwritten;
-#else
-#if defined(SOLARIS) || defined(FREEBSD)
+#elif defined(SOLARIS)
                 if (pos > offset) {
                     /* we actually have sent sth., adjust counters and keep trying */
                     len = pos - offset;
                     offset = pos;
                 }
-#endif /* defined(SOLARIS) || defined(FREEBSD) */
 #endif /* HAVE_SENDFILEV */
 
                 if (dsi_peek(dsi) != 0) {
@@ -426,6 +441,18 @@ ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const siz
             vec[0].sfv_off += len;
             vec[0].sfv_len -= len;
         }
+#elif defined(FREEBSD)
+        if (hdrp) {
+            if (len >= iovec.iov_len) {
+                hdrp = NULL;
+                len -= iovec.iov_len;   /* len now contains how much sendfile() actually sent from the file */
+            } else {
+                iovec.iov_len -= len;
+                iovec.iov_base += len;
+                len = 0;
+            }
+        }
+        pos += len;
 #endif  /* HAVE_SENDFILEV */
         LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len);
         written += len;
@@ -471,10 +498,6 @@ size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
           stored += len;
       } else { /* eof or error */
           /* don't log EOF error if it's just after connect (OSX 10.3 probe) */
-#if 0
-          if (errno == ECONNRESET)
-              dsi->flags |= DSI_GOT_ECONNRESET;
-#endif
           if (len || stored || dsi->read_count) {
               if (! (dsi->flags & DSI_DISCONNECTED)) {
                   LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
@@ -500,6 +523,7 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
 {
   char block[DSI_BLOCKSIZ];
   struct iovec iov[2];
+  int iovecs = 2;
   size_t towrite;
   ssize_t len;
 
@@ -526,7 +550,7 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
   towrite = sizeof(block) + length;
   dsi->write_count += towrite;
   while (towrite > 0) {
-      if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || (len == 0))
+      if (((len = writev(dsi->socket, iov, iovecs)) == -1 && errno == EINTR) || (len == 0))
           continue;
     
       if ((size_t)len == towrite) /* wrote everything out */
@@ -547,12 +571,13 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
           iov[0].iov_base = (char *) iov[0].iov_base + len;
           iov[0].iov_len -= len;
       } else { /* skip to data */
-          if (iov[0].iov_len) {
+          if (iovecs == 2) {
+              iovecs = 1;
               len -= iov[0].iov_len;
-              iov[0].iov_len = 0;
+              iov[0] = iov[1];
           }
-          iov[1].iov_base = (char *) iov[1].iov_base + len;
-          iov[1].iov_len -= len;
+          iov[0].iov_base = (char *) iov[0].iov_base + len;
+          iov[0].iov_len -= len;
       }
   }
 
index e06e87d92420187948611b9d044995ab158065c6..f73147a81a9565309bcbe518042edf1f6865e106 100644 (file)
@@ -103,9 +103,29 @@ static void dsi_init_buffer(DSI *dsi)
     dsi->end = dsi->buffer + (dsi->dsireadbuf * dsi->server_quantum);
 }
 
+/*!
+ * Free any allocated ressources of the master afpd DSI objects and close server socket
+ */
+void dsi_free(DSI *dsi)
+{
+    close(dsi->serversock);
+    dsi->serversock = -1;
+
+    free(dsi->commands);
+    dsi->commands = NULL;
+
+    free(dsi->buffer);
+    dsi->buffer = NULL;
+
+#ifdef USE_ZEROCONF
+    free(dsi->bonjourname);
+    dsi->bonjourname = NULL;
+#endif
+}
+
 static struct itimerval itimer;
 /* accept the socket and do a little sanity checking */
-static int dsi_tcp_open(DSI *dsi)
+static pid_t dsi_tcp_open(DSI *dsi)
 {
     pid_t pid;
     SOCKLEN_T len;
index 40d0c0ad4cb43376dc0421c4b8fd504708fc15e2..a283d40b82670fbb1b3172788fca1e8bfa62a248 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_tickle.c,v 1.8 2009-10-25 06:13:11 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index 1cf25e64c6516ea65e4144f63eafcd625fdb72d2..65a479e1dac4a89d09fd5785b623715f382d6988 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dsi_write.c,v 1.5 2009-10-20 04:31:41 didg Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
index b44796bb24e30394d70cfd84c81784255b9068e5..f1ff81567aefcccb8da5c45730a8256a40f49920 100644 (file)
@@ -3,7 +3,6 @@
    @file       dictionary.c
    @author     N. Devillard
    @date       Sep 2007
-   @version    $Revision: 1.27 $
    @brief      Implements a dictionary for string variables.
 
    This module implements a simple dictionary object, i.e. a list
 /*--------------------------------------------------------------------------*/
 
 /*
-       $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $
-       $Revision: 1.27 $
 */
 /*---------------------------------------------------------------------------
                                                                Includes
  ---------------------------------------------------------------------------*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <atalk/dictionary.h>
 #include <atalk/compat.h>
 
index 6e89472b34be54aff750f48a3c53b073fedd04b4..93e14b6b46b55b6b29bbe7802b68ca70b416e881 100644 (file)
@@ -8,6 +8,10 @@
 */
 
 /*---------------------------- Includes ------------------------------------*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include <ctype.h>
 
 #include <atalk/iniparser.h>
@@ -474,8 +478,7 @@ static line_status iniparser_line(
         strcpy(section, strstrip(section));
         strcpy(section, section);
         sta = LINE_SECTION ;
-    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
-           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
+    } else if (sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
            ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
         /* Usual key=value, with or without comments */
         strcpy(key, strstrip(key));
@@ -529,7 +532,6 @@ dictionary * iniparser_load(const char * ininame)
     char line    [ASCIILINESZ+1] ;
     char section [ASCIILINESZ+1] ;
     char key     [ASCIILINESZ+1] ;
-    char tmp     [ASCIILINESZ+1] ;
     char val     [ASCIILINESZ+1] ;
 
     int  last=0 ;
@@ -571,14 +573,6 @@ dictionary * iniparser_load(const char * ininame)
         len = (int)strlen(line)-1;
         if (len==0)
             continue;
-        /* Safety check against buffer overflows */
-        if (line[len]!='\n') {
-            LOG(log_error, logtype_default, "iniparser: input line too long in \"%s\" (lineno: %d)",
-                ininame, lineno);
-            dictionary_del(dict);
-            fclose(in);
-            return NULL ;
-        }
         /* Get rid of \n and spaces at end of line */
         while ((len>=0) &&
                 ((line[len]=='\n') || (isspace(line[len])))) {
diff --git a/libatalk/libatalk-3.0.1dev.abi b/libatalk/libatalk-3.0.1dev.abi
new file mode 100644 (file)
index 0000000..f671fca
--- /dev/null
@@ -0,0 +1,554 @@
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_getsession: int (DSI *, server_child *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_client_uds: int (const char *)
+ipc_server_read: int (server_child *, int)
+ipc_server_uds: int (const char *)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+lchdir: int (const char *)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = -1}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *, void (*)(const AFPObj *, struct vol *))
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+lstatat: int (int, const char *, struct stat *)
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+reconnect_ipc: int (AFPObj *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child *, int, pid_t, int)
+server_child_alloc: server_child *(const int, const int)
+server_child_free: void (server_child *)
+server_child_kill: void (server_child *, int, int)
+server_child_kill_one_by_id: void (server_child *, int, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child *, const int, pid_t)
+server_child_setup: void (server_child *, const int, void (*)(const pid_t))
+server_child_transfer_session: int (server_child *, int, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const char *, mode_t, struct stat *, mode_t)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.2.abi b/libatalk/libatalk-3.0.2.abi
new file mode 100644 (file)
index 0000000..8e73229
--- /dev/null
@@ -0,0 +1,565 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_client_uds: int (const char *)
+ipc_server_read: int (server_child *, int)
+ipc_server_uds: int (const char *)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+reconnect_ipc: int (AFPObj *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child *, int, pid_t, int)
+server_child_alloc: server_child *(const int, const int)
+server_child_free: void (server_child *)
+server_child_kill: void (server_child *, int, int)
+server_child_kill_one_by_id: void (server_child *, int, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child *, const int, pid_t)
+server_child_setup: void (server_child *, const int, void (*)(const pid_t))
+server_child_transfer_session: int (server_child *, int, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.3.abi b/libatalk/libatalk-3.0.3.abi
new file mode 100644 (file)
index 0000000..17e9539
--- /dev/null
@@ -0,0 +1,566 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
diff --git a/libatalk/libatalk-3.0.4.abi b/libatalk/libatalk-3.0.4.abi
new file mode 100644 (file)
index 0000000..33e4f92
--- /dev/null
@@ -0,0 +1,566 @@
+acl_ldap_freeconfig: void (void)
+acl_ldap_readconfig: int (dictionary *)
+ad_close: int (struct adouble *, int)
+ad_convert: int (const char *, const struct stat *, const struct vol *, const char **)
+ad_copy_header: int (struct adouble *, struct adouble *)
+add_cachebyname: int (const char *, const uuidp_t, const uuidtype_t, const long unsigned int)
+add_cachebyuuid: int (uuidp_t, const char *, uuidtype_t, const long unsigned int)
+add_charset: charset_t (const char *)
+ad_dir: char *(const char *)
+ad_dtruncate: int (struct adouble *, const off_t)
+adflags2logstr: const char *(int)
+ad_flush: int (struct adouble *)
+ad_forcegetid: uint32_t (struct adouble *)
+adf_pread: ssize_t (struct ad_fd *, void *, size_t, off_t)
+adf_pwrite: ssize_t (struct ad_fd *, const void *, size_t, off_t)
+ad_getattr: int (const struct adouble *, uint16_t *)
+ad_getdate: int (const struct adouble *, unsigned int, uint32_t *)
+ad_getentryoff: off_t (const struct adouble *, int)
+ad_getfuid: uid_t (void)
+ad_getid: uint32_t (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *)
+ad_hf_mode: mode_t (mode_t)
+ad_init: void (struct adouble *, const struct vol *)
+ad_init_old: void (struct adouble *, int, int)
+ad_lock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_metadata: int (const char *, int, struct adouble *)
+ad_metadataat: int (int, const char *, int, struct adouble *)
+ad_mkdir: int (const char *, mode_t)
+ad_mode: int (const char *, mode_t)
+ad_open: int (struct adouble *, const char *, int, ...)
+ad_openat: int (struct adouble *, int, const char *, int, ...)
+ad_openforks: uint16_t (struct adouble *, uint16_t)
+ad_path: const char *(const char *, int)
+ad_path_ea: const char *(const char *, int)
+ad_path_osx: const char *(const char *, int)
+ad_read: ssize_t (struct adouble *, const uint32_t, off_t, char *, const size_t)
+ad_readfile_init: int (const struct adouble *, const int, off_t *, const int)
+ad_rebuild_adouble_header_ea: int (struct adouble *)
+ad_rebuild_adouble_header_v2: int (struct adouble *)
+ad_refresh: int (const char *, struct adouble *)
+ad_reso_size: off_t (const char *, int, struct adouble *)
+ad_rtruncate: int (struct adouble *, const char *, const off_t)
+ad_setattr: int (const struct adouble *, const uint16_t)
+ad_setdate: int (struct adouble *, unsigned int, uint32_t)
+ad_setfuid: int (const uid_t)
+ad_setid: int (struct adouble *, const dev_t, const ino_t, const uint32_t, const cnid_t, const void *)
+ad_setname: int (struct adouble *, const char *)
+ad_size: off_t (const struct adouble *, const uint32_t)
+ad_stat: int (const char *, struct stat *)
+ad_testlock: int (struct adouble *, int, const off_t)
+ad_tmplock: int (struct adouble *, uint32_t, int, off_t, off_t, int)
+ad_unlock: void (struct adouble *, const int, int)
+ad_valid_header_osx: int (const char *)
+ad_write: ssize_t (struct adouble *, uint32_t, off_t, int, const char *, size_t)
+afp_config_free: void (AFPObj *)
+afp_config_parse: int (AFPObj *, char *)
+allow_severity: 5
+apply_ip_mask: void (struct sockaddr *, int)
+atalk_iconv: size_t (atalk_iconv_t, const char **, size_t *, char **, size_t *)
+atalk_iconv_close: int (atalk_iconv_t)
+atalk_iconv_open: atalk_iconv_t (const char *, const char *)
+atalk_register_charset: int (struct charset_functions *)
+balloc: int (bstring, int)
+ballocmin: int (bstring, int)
+basename_safe: const char *(const char *)
+bassign: int (bstring, const_bstring)
+bassignblk: int (bstring, const void *, int)
+bassigncstr: int (bstring, const char *)
+bassignformat: int (bstring, const char *, ...)
+bassigngets: int (bstring, bNgetc, void *, char)
+bassignmidstr: int (bstring, const_bstring, int, int)
+bcatblk: int (bstring, const void *, int)
+bcatcstr: int (bstring, const char *)
+bconcat: int (bstring, const_bstring)
+bconchar: int (bstring, char)
+bcstrfree: int (char *)
+bdelete: int (bstring, int, int)
+bdestroy: int (bstring)
+become_root: void (void)
+bfindreplace: int (bstring, const_bstring, const_bstring, int)
+bfindreplacecaseless: int (bstring, const_bstring, const_bstring, int)
+bformat: bstring (const char *, ...)
+bformata: int (bstring, const char *, ...)
+bfromcstr: bstring (const char *)
+bfromcstralloc: bstring (int, const char *)
+bgetsa: int (bstring, bNgetc, void *, char)
+bgetstream: bstring (bNgetc, void *, char)
+binchr: int (const_bstring, int, const_bstring)
+binchrr: int (const_bstring, int, const_bstring)
+binsert: int (bstring, int, const_bstring, unsigned char)
+binsertch: int (bstring, int, int, unsigned char)
+binstr: int (const_bstring, int, const_bstring)
+binstrcaseless: int (const_bstring, int, const_bstring)
+binstrr: int (const_bstring, int, const_bstring)
+binstrrcaseless: int (const_bstring, int, const_bstring)
+biseq: int (const_bstring, const_bstring)
+biseqcaseless: int (const_bstring, const_bstring)
+biseqcstr: int (const_bstring, const char *)
+biseqcstrcaseless: int (const_bstring, const char *)
+bisstemeqblk: int (const_bstring, const void *, int)
+bisstemeqcaselessblk: int (const_bstring, const void *, int)
+bjoin: bstring (const struct bstrList *, const_bstring)
+bjoinInv: bstring (const struct bstrList *, const_bstring)
+blk2bstr: bstring (const void *, int)
+bltrimws: int (bstring)
+bmidstr: bstring (const_bstring, int, int)
+bninchr: int (const_bstring, int, const_bstring)
+bninchrr: int (const_bstring, int, const_bstring)
+bpattern: int (bstring, int)
+bread: bstring (bNread, void *)
+breada: int (bstring, bNread, void *)
+brefcstr: bstring (char *)
+breplace: int (bstring, int, int, const_bstring, unsigned char)
+brtrimws: int (bstring)
+bsbufflength: int (struct bStream *, int)
+bsclose: void *(struct bStream *)
+bseof: int (const struct bStream *)
+bsetstr: int (bstring, int, const_bstring, unsigned char)
+bsopen: struct bStream *(bNread, void *)
+bspeek: int (bstring, const struct bStream *)
+bsplit: struct bstrList *(const_bstring, unsigned char)
+bsplitcb: int (const_bstring, unsigned char, int, int (*)(void *, int, int), void *)
+bsplits: struct bstrList *(const_bstring, const_bstring)
+bsplitscb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsplitstr: struct bstrList *(const_bstring, const_bstring)
+bsplitstrcb: int (const_bstring, const_bstring, int, int (*)(void *, int, int), void *)
+bsread: int (bstring, struct bStream *, int)
+bsreada: int (bstring, struct bStream *, int)
+bsreadln: int (bstring, struct bStream *, char)
+bsreadlna: int (bstring, struct bStream *, char)
+bsreadlns: int (bstring, struct bStream *, const_bstring)
+bsreadlnsa: int (bstring, struct bStream *, const_bstring)
+bssplitscb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bssplitstrcb: int (struct bStream *, const_bstring, int (*)(void *, int, const_bstring), void *)
+bstr2cstr: char *(const_bstring, char)
+bstrchrp: int (const_bstring, int, int)
+bstrcmp: int (const_bstring, const_bstring)
+bstrcpy: bstring (const_bstring)
+bstricmp: int (const_bstring, const_bstring)
+bstrListAlloc: int (struct bstrList *, int)
+bstrListAllocMin: int (struct bstrList *, int)
+bstrListCreate: struct bstrList *(void)
+bstrListCreateMin: struct bstrList *(int)
+bstrListDestroy: int (struct bstrList *)
+bstrListPop: bstring (struct bstrList *)
+bstrListPush: int (struct bstrList *, bstring)
+bstrncmp: int (const_bstring, const_bstring, int)
+bstrnicmp: int (const_bstring, const_bstring, int)
+bstrrchrp: int (const_bstring, int, int)
+bsunread: int (struct bStream *, const_bstring)
+btolower: int (bstring)
+btoupper: int (bstring)
+btrimws: int (bstring)
+btrunc: int (bstring, int)
+bunrefcstr: int (bstring)
+bvcformata: int (bstring, int, const char *, struct __va_list_tag *)
+charset_decompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_mac_centraleurope: {name = "MAC_CENTRALEUROPE", kTextEncoding = 29, pull = <mac_centraleurope_pull>, push = <mac_centraleurope_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_chinese_simp: {name = "MAC_CHINESE_SIMP", kTextEncoding = 25, pull = <mac_chinese_simp_pull>, push = <mac_chinese_simp_push>, flags = 85, iname = "EUC-CN", prev = 0x0, next = 0x0}
+charset_mac_chinese_trad: {name = "MAC_CHINESE_TRAD", kTextEncoding = 2, pull = <mac_chinese_trad_pull>, push = <mac_chinese_trad_push>, flags = 85, iname = "BIG-5", prev = 0x0, next = 0x0}
+charset_mac_cyrillic: {name = "MAC_CYRILLIC", kTextEncoding = 7, pull = <mac_cyrillic_pull>, push = <mac_cyrillic_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_greek: {name = "MAC_GREEK", kTextEncoding = 6, pull = <mac_greek_pull>, push = <mac_greek_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_hebrew: {name = "MAC_HEBREW", kTextEncoding = 5, pull = <mac_hebrew_pull>, push = <mac_hebrew_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_japanese: {name = "MAC_JAPANESE", kTextEncoding = 1, pull = <mac_japanese_pull>, push = <mac_japanese_push>, flags = 85, iname = "SHIFT_JIS", prev = 0x0, next = 0x0}
+charset_mac_korean: {name = "MAC_KOREAN", kTextEncoding = 3, pull = <mac_korean_pull>, push = <mac_korean_push>, flags = 85, iname = "EUC-KR", prev = 0x0, next = 0x0}
+charset_mac_roman: {name = "MAC_ROMAN", kTextEncoding = 0, pull = <mac_roman_pull>, push = <mac_roman_push>, flags = 21, iname = 0x0, prev = 0x0, next = 0x0}
+charset_mac_turkish: {name = "MAC_TURKISH", kTextEncoding = 35, pull = <mac_turkish_pull>, push = <mac_turkish_push>, flags = 17, iname = 0x0, prev = 0x0, next = 0x0}
+charset_precompose: size_t (charset_t, char *, size_t, char *, size_t)
+charset_strlower: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_strupper: size_t (charset_t, const char *, size_t, char *, size_t)
+charset_to_ucs2_allocate: size_t (charset_t, uint16_t **, const char *)
+charset_to_utf8_allocate: size_t (charset_t, char **, const char *)
+charset_utf8: {name = "UTF8", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 22, iname = 0x0, prev = 0x0, next = 0x0}
+charset_utf8_mac: {name = "UTF8-MAC", kTextEncoding = 134217987, pull = <utf8_pull>, push = <utf8_push>, flags = 27, iname = 0x0, prev = 0x0, next = 0x0}
+check_lockfile: int (const char *, const char *)
+cjk_char_pull: size_t (uint16_t, uint16_t *, const uint32_t *)
+cjk_char_push: size_t (uint16_t, uint8_t *)
+cjk_compose: uint16_t (uint16_t, uint16_t, const uint32_t *, size_t)
+cjk_compose_seq: uint16_t (const uint16_t *, size_t *, const uint32_t *, size_t)
+cjk_generic_pull: size_t (size_t (*)(uint16_t *, const uint8_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_generic_push: size_t (size_t (*)(uint8_t *, const uint16_t *, size_t *), void *, char **, size_t *, char **, size_t *)
+cjk_lookup: uint16_t (uint16_t, const cjk_index_t *, const uint16_t *)
+cnid_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, const char *, const size_t, cnid_t)
+cnid_close: void (struct _cnid_db *)
+cnid_dbd_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_close: void (struct _cnid_db *)
+cnid_dbd_delete: int (struct _cnid_db *, const cnid_t)
+cnid_dbd_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_dbd_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_dbd_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_dbd_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_module: {name = "dbd", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_dbd_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_dbd_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_dbd_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_dbd_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_dbd_wipe: int (struct _cnid_db *)
+cnid_delete: int (struct _cnid_db *, cnid_t)
+cnid_find: int (struct _cnid_db *, const char *, size_t, void *, size_t)
+cnid_get: cnid_t (struct _cnid_db *, const cnid_t, char *, const size_t)
+cnid_getstamp: int (struct _cnid_db *, void *, const size_t)
+cnid_init: void (void)
+cnid_last_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_last_close: void (struct _cnid_db *)
+cnid_last_delete: int (struct _cnid_db *, const cnid_t)
+cnid_last_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_last_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_last_module: {name = "last", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 0}
+cnid_last_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_last_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_last_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_lookup: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t)
+cnid_open: struct _cnid_db *(const char *, mode_t, char *, int, const char *, const char *)
+cnid_rebuild_add: cnid_t (struct _cnid_db *, const struct stat *, const cnid_t, char *, const size_t, cnid_t)
+cnid_register: void (struct _cnid_module *)
+cnid_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_add: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t, cnid_t)
+cnid_tdb_close: void (struct _cnid_db *)
+cnid_tdb_delete: int (struct _cnid_db *, const cnid_t)
+cnid_tdb_get: cnid_t (struct _cnid_db *, cnid_t, const char *, size_t)
+cnid_tdb_lookup: cnid_t (struct _cnid_db *, const struct stat *, cnid_t, const char *, size_t)
+cnid_tdb_module: {name = "tdb", db_list = {next = 0x0, prev = 0x0}, cnid_open = 0, flags = 12}
+cnid_tdb_open: struct _cnid_db *(struct cnid_open_args *)
+cnid_tdb_resolve: char *(struct _cnid_db *, cnid_t *, void *, size_t)
+cnid_tdb_update: int (struct _cnid_db *, cnid_t, const struct stat *, cnid_t, const char *, size_t)
+cnid_update: int (struct _cnid_db *, const cnid_t, const struct stat *, const cnid_t, char *, const size_t)
+cnid_wipe: int (struct _cnid_db *)
+compare_ip: int (const struct sockaddr *, const struct sockaddr *)
+convert_charset: size_t (charset_t, charset_t, charset_t, const char *, size_t, char *, size_t, uint16_t *)
+convert_string: size_t (charset_t, charset_t, const void *, size_t, void *, size_t)
+convert_string_allocate: size_t (charset_t, charset_t, const void *, size_t, char **)
+copy_ea: int (const char *, int, const char *, const char *, mode_t)
+copy_file: int (int, const char *, const char *, mode_t)
+copy_file_fd: int (int, int)
+copy_fork: int (int, struct adouble *, struct adouble *)
+create_lockfile: int (const char *, const char *)
+daemonize: int (int, int)
+decompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+deny_severity: 3
+dequeue: void *(q_t *)
+_diacasemap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 231, 203, 229, 128, 204, 129, 130, 131, 233, 230, 232, 234, 237, 235, 236, 132, 238, 241, 239, 133, 205, 242, 244, 243, 134, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 198, 183, 184, 184, 186, 187, 188, 189, 174, 175, 192, 193, 194, 195, 196, 197, 198, 199...}
+_dialowermap: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 138, 140, 141, 142, 150, 154, 159, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 132, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 190, 191, 176, 177, 178, 179, 180, 181, 198, 183, 185, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...}
+dictionary_del: void (dictionary *)
+dictionary_dump: void (dictionary *, FILE *)
+dictionary_get: const char *(const dictionary *, const char *, const char *, const char *)
+dictionary_hash: unsigned int (char *)
+dictionary_new: dictionary *(int)
+dictionary_set: int (dictionary *, char *, char *, char *)
+dictionary_unset: void (dictionary *, char *, char *)
+dir_rx_set: int (mode_t)
+dsi_attention: int (DSI *, AFPUserBytes)
+dsi_close: void (DSI *)
+dsi_cmdreply: int (DSI *, const int)
+dsi_disconnect: int (DSI *)
+dsi_free: void (DSI *)
+dsi_getsession: int (DSI *, server_child_t *, int, afp_child_t **)
+dsi_getstatus: void (DSI *)
+dsi_init: DSI *(AFPObj *, const char *, const char *, const char *)
+dsi_opensession: void (DSI *)
+dsi_read: ssize_t (DSI *, void *, const size_t)
+dsi_readdone: void (DSI *)
+dsi_readinit: ssize_t (DSI *, void *, const size_t, const size_t, const int)
+dsi_stream_read: size_t (DSI *, void *, const size_t)
+dsi_stream_read_file: ssize_t (DSI *, const int, off_t, const size_t, const int)
+dsi_stream_receive: int (DSI *)
+dsi_stream_send: int (DSI *, void *, size_t)
+dsi_stream_write: ssize_t (DSI *, void *, const size_t, int)
+dsi_tcp_init: int (DSI *, const char *, const char *, const char *)
+dsi_tickle: int (DSI *)
+dsi_write: size_t (DSI *, void *, const size_t)
+dsi_writeflush: void (DSI *)
+dsi_writeinit: size_t (DSI *, void *, const size_t)
+ea_chmod_dir: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chmod_file: int (const struct vol *, const char *, mode_t, struct stat *)
+ea_chown: int (const struct vol *, const char *, uid_t, gid_t)
+ea_close: int (struct ea *)
+ea_copyfile: int (const struct vol *, int, const char *, const char *)
+ea_deletefile: int (const struct vol *, int, const char *)
+ea_open: int (const struct vol *, const char *, eaflags_t, struct ea *)
+ea_openat: int (const struct vol *, int, const char *, eaflags_t, struct ea *)
+ea_path: char *(const struct ea *, const char *, int)
+ea_renamefile: int (const struct vol *, int, const char *, const char *)
+enqueue: qnode_t *(q_t *, void *)
+fault_setup: void (void (*)(void *))
+fdset_add_fd: void (int, struct pollfd **, struct polldata **, int *, int *, int, enum fdtype, void *)
+fdset_del_fd: void (struct pollfd **, struct polldata **, int *, int *, int)
+find_charset_functions: struct charset_functions *(const char *)
+_fini: <text variable, no debug info>
+free_charset_names: void (void)
+freeifacelist: void (char **)
+fullpathname: const char *(const char *)
+getcwdpath: const char *(void)
+getdefextmap: struct extmap *(void)
+get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+getextmap: struct extmap *(const char *)
+getifacelist: char **(void)
+getip_port: unsigned int (const struct sockaddr *)
+getip_string: const char *(const struct sockaddr *)
+getnamefromuuid: int (const uuidp_t, char **, uuidtype_t *)
+getuuidfromname: int (const char *, uuidtype_t, unsigned char *)
+getvolbyname: struct vol *(const char *)
+getvolbypath: struct vol *(AFPObj *, const char *)
+getvolbyvid: struct vol *(const uint16_t)
+getvolumes: struct vol *(void)
+gmem: int (gid_t, int, gid_t *)
+iniparser_dump: void (const dictionary *, FILE *)
+iniparser_dump_ini: void (const dictionary *, FILE *)
+iniparser_find_entry: int (const dictionary *, const char *)
+iniparser_freedict: void (dictionary *)
+iniparser_getboolean: int (const dictionary *, const char *, const char *, int)
+iniparser_getdouble: double (const dictionary *, const char *, const char *, double)
+iniparser_getint: int (const dictionary *, const char *, const char *, int)
+iniparser_getnsec: int (const dictionary *)
+iniparser_getsecname: const char *(const dictionary *, int)
+iniparser_getstrdup: char *(const dictionary *, const char *, const char *, const char *)
+iniparser_getstring: const char *(const dictionary *, const char *, const char *, const char *)
+iniparser_load: dictionary *(const char *)
+iniparser_set: int (dictionary *, char *, char *, char *)
+iniparser_unset: void (dictionary *, char *, char *)
+_init: <text variable, no debug info>
+init_iconv: void (void)
+initline: void (int, char *)
+initvol_vfs: void (struct vol *)
+ipc_child_state: int (AFPObj *, uint16_t)
+ipc_child_write: int (int, uint16_t, int, void *)
+ipc_server_read: int (server_child_t *, int)
+islower_sp: int (uint32_t)
+islower_w: int (uint16_t)
+isupper_sp: int (uint32_t)
+isupper_w: int (uint16_t)
+ldap_auth_dn: 0x0
+ldap_auth_method: 0
+ldap_auth_pw: 0x0
+ldap_config_valid: 0
+ldap_getnamefromuuid: int (const char *, char **, uuidtype_t *)
+ldap_getuuidfromname: int (const char *, uuidtype_t, char **)
+ldap_group_attr: 0x0
+ldap_groupbase: 0x0
+ldap_groupscope: 0
+ldap_name_attr: 0x0
+ldap_prefs: {{pref = 0x0, name = "ldap server", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth method", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap auth dn", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap auth pw", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap userbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap userscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupbase", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap groupscope", strorint = 1, intfromarray = 1, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uuid string", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap name attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap group attr", strorint = 0, intfromarray = 0, valid = -1, valid_save = -1}, {pref = 0x0, name = "ldap uid attr", strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}, {pref = 0x0, name = "ldap uuid encoding", strorint = 1, intfromarray = 1, valid = 0, valid_save = 0}, {pref = 0x0, name = 0x0, strorint = 0, intfromarray = 0, valid = 0, valid_save = 0}}
+ldap_server: 0x0
+ldap_uid_attr: 0x0
+ldap_userbase: 0x0
+ldap_userscope: 0
+ldap_uuid_attr: 0x0
+ldap_uuid_encoding: 0
+ldap_uuid_string: 0x0
+list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+load_charset: int (struct vol *)
+load_volumes: int (AFPObj *)
+localuuid_from_id: void (unsigned char *, uuidtype_t, unsigned int)
+lock_reg: int (int, int, int, off_t, int, off_t)
+log_config: {inited = false, syslog_opened = false, console = false, processname = '\0' <repeats 15 times>, syslog_facility = 0, syslog_display_options = 0}
+make_log_entry: void (enum loglevels, enum logtypes, const char *, int, char *, ...)
+make_tdb_data: unsigned char *(uint32_t, const struct stat *, const cnid_t, const char *, const size_t)
+mb_generic_pull: size_t (int (*)(uint16_t *, const unsigned char *), void *, char **, size_t *, char **, size_t *)
+mb_generic_push: size_t (int (*)(unsigned char *, uint16_t), void *, char **, size_t *, char **, size_t *)
+netatalk_panic: void (const char *)
+netatalk_rmdir: int (int, const char *)
+netatalk_rmdir_all_errors: int (int, const char *)
+netatalk_unlink: int (const char *)
+netatalk_unlinkat: int (int, const char *)
+nftw: int (const char *, nftw_func_t, dir_notification_func_t, int, int)
+ochdir: int (const char *, int)
+ochmod: int (char *, mode_t, const struct stat *, int)
+ochown: int (const char *, uid_t, gid_t, int)
+opendirat: DIR *(int, const char *)
+openflags2logstr: const char *(int)
+ostat: int (const char *, struct stat *, int)
+ostatat: int (int, const char *, struct stat *, int)
+parseline: int (int, char *)
+posix_chmod: int (const char *, mode_t)
+posix_fchmod: int (int, mode_t)
+precompose_w: size_t (uint16_t *, size_t, uint16_t *, size_t *)
+prefs_array: {{pref = "ldap auth method", valuestring = "none", value = 0}, {pref = "ldap auth method", valuestring = "simple", value = 128}, {pref = "ldap auth method", valuestring = "sasl", value = 163}, {pref = "ldap userscope", valuestring = "base", value = 0}, {pref = "ldap userscope", valuestring = "one", value = 1}, {pref = "ldap userscope", valuestring = "sub", value = 2}, {pref = "ldap groupscope", valuestring = "base", value = 0}, {pref = "ldap groupscope", valuestring = "one", value = 1}, {pref = "ldap groupscope", valuestring = "sub", value = 2}, {pref = "ldap uuid encoding", valuestring = "ms-guid", value = 1}, {pref = "ldap uuid encoding", valuestring = "string", value = 0}, {pref = 0x0, valuestring = 0x0, value = 0}}
+prequeue: qnode_t *(q_t *, void *)
+print_groups: const char *(int, gid_t *)
+queue_destroy: void (q_t *, void (*)(void *))
+queue_init: q_t *(void)
+randombytes: void (void *, int)
+readt: ssize_t (int, void *, const size_t, int, int)
+realpath_safe: char *(const char *)
+recv_fd: int (int, int)
+rel_path_in_vol: bstring (const char *, const char *)
+remove_acl_vfs: int (const char *)
+remove_ea: int (const struct vol *, const char *, const char *, int)
+run_cmd: int (const char *, char **)
+search_cachebyname: int (const char *, uuidtype_t *, unsigned char *)
+search_cachebyuuid: int (uuidp_t, char **, uuidtype_t *)
+send_fd: int (int, int)
+server_child_add: afp_child_t *(server_child_t *, pid_t, int)
+server_child_alloc: server_child_t *(int)
+server_child_free: void (server_child_t *)
+server_child_kill: void (server_child_t *, int)
+server_child_kill_one_by_id: void (server_child_t *, pid_t, uid_t, uint32_t, char *, uint32_t)
+server_child_remove: int (server_child_t *, pid_t)
+server_child_resolve: afp_child_t *(server_child_t *, id_t)
+server_child_transfer_session: int (server_child_t *, pid_t, uid_t, int, uint16_t)
+server_lock: pid_t (char *, char *, int)
+server_reset_signal: void (void)
+set_charset_name: int (charset_t, const char *)
+set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+setfilmode: int (const struct vol *, const char *, mode_t, struct stat *)
+set_groups: int (AFPObj *, struct passwd *)
+setnonblock: int (int, int)
+set_processname: void (const char *)
+setuplog: void (const char *, const char *)
+statat: int (int, const char *, struct stat *)
+strcasechr_sp: uint16_t *(const uint16_t *, uint32_t)
+strcasechr_w: uint16_t *(const uint16_t *, uint16_t)
+strcasecmp_w: int (const uint16_t *, const uint16_t *)
+strcasestr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strcat_w: uint16_t *(uint16_t *, const uint16_t *)
+strchr_w: uint16_t *(const uint16_t *, uint16_t)
+strcmp_w: int (const uint16_t *, const uint16_t *)
+strdiacasecmp: int (const char *, const char *)
+strdup_w: uint16_t *(const uint16_t *)
+stripped_slashes_basename: char *(char *)
+strlcat: size_t (char *, const char *, size_t)
+strlcpy: size_t (char *, const char *, size_t)
+strlen_w: size_t (const uint16_t *)
+strlower_w: int (uint16_t *)
+strncasecmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncat_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strncmp_w: int (const uint16_t *, const uint16_t *, size_t)
+strncpy_w: uint16_t *(uint16_t *, const uint16_t *, const size_t)
+strndiacasecmp: int (const char *, const char *, size_t)
+strndup_w: uint16_t *(const uint16_t *, size_t)
+strnlen_w: size_t (const uint16_t *, size_t)
+strstr_w: uint16_t *(const uint16_t *, const uint16_t *)
+strtok_quote: char *(char *, const char *)
+strupper_w: int (uint16_t *)
+sys_ea_copyfile: int (const struct vol *, int, const char *, const char *)
+sys_fgetxattr: ssize_t (int, const char *, void *, size_t)
+sys_fsetxattr: int (int, const char *, const void *, size_t, int)
+sys_ftruncate: int (int, off_t)
+sys_get_eacontent: int (const struct vol *, char *, size_t *, const char *, int, const char *, int)
+sys_get_easize: int (const struct vol *, char *, size_t *, const char *, int, const char *)
+sys_getxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_getxattrfd: int (int, const char *, int, ...)
+sys_lgetxattr: ssize_t (const char *, const char *, void *, size_t)
+sys_list_eas: int (const struct vol *, char *, size_t *, const char *, int)
+sys_listxattr: ssize_t (const char *, char *, size_t)
+sys_llistxattr: ssize_t (const char *, char *, size_t)
+sys_lremovexattr: int (const char *, const char *)
+sys_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+sys_remove_ea: int (const struct vol *, const char *, const char *, int)
+sys_removexattr: int (const char *, const char *)
+sys_sendfile: ssize_t (int, int, off_t *, size_t)
+sys_set_ea: int (const struct vol *, const char *, const char *, const char *, size_t, int)
+sys_setxattr: int (const char *, const char *, const void *, size_t, int)
+tdb_add_flags: void (struct tdb_context *, unsigned int)
+tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *)
+tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t)
+tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA)
+tdb_brlock: int (struct tdb_context *, tdb_off_t, int, int, int, size_t)
+tdb_brlock_upgrade: int (struct tdb_context *, tdb_off_t, size_t)
+tdb_chainlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock: int (struct tdb_context *, TDB_DATA)
+tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA)
+tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_close: int (struct tdb_context *)
+tdb_convert: void *(void *, uint32_t)
+tdb_delete: int (struct tdb_context *, TDB_DATA)
+tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_dump_all: void (struct tdb_context *)
+tdb_enable_seqnum: void (struct tdb_context *)
+tdb_error: enum TDB_ERROR (struct tdb_context *)
+tdb_errorstr: const char *(struct tdb_context *)
+tdb_exists: int (struct tdb_context *, TDB_DATA)
+tdb_expand: int (struct tdb_context *, tdb_off_t)
+tdb_fd: int (struct tdb_context *)
+tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *)
+tdb_firstkey: TDB_DATA (struct tdb_context *)
+tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_freelist_size: int (struct tdb_context *)
+tdb_get_flags: int (struct tdb_context *)
+tdb_get_logging_private: void *(struct tdb_context *)
+tdb_get_seqnum: int (struct tdb_context *)
+tdb_hash_size: int (struct tdb_context *)
+tdb_increment_seqnum_nonblock: void (struct tdb_context *)
+tdb_io_init: void (struct tdb_context *)
+tdb_lock: int (struct tdb_context *, int, int)
+tdb_lockall: int (struct tdb_context *)
+tdb_lockall_mark: int (struct tdb_context *)
+tdb_lockall_nonblock: int (struct tdb_context *)
+tdb_lockall_read: int (struct tdb_context *)
+tdb_lockall_read_nonblock: int (struct tdb_context *)
+tdb_lockall_unmark: int (struct tdb_context *)
+tdb_lock_nonblock: int (struct tdb_context *, int, int)
+tdb_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_log_fn: tdb_log_func (struct tdb_context *)
+tdb_map_size: size_t (struct tdb_context *)
+tdb_mmap: void (struct tdb_context *)
+tdb_munmap: int (struct tdb_context *)
+tdb_name: const char *(struct tdb_context *)
+tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA)
+tdb_null: {dptr = 0x0, dsize = 0}
+tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *)
+tdb_open: struct tdb_context *(const char *, int, int, int, mode_t)
+tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func)
+tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *)
+tdb_printfreelist: int (struct tdb_context *)
+tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *)
+tdb_remove_flags: void (struct tdb_context *, unsigned int)
+tdb_reopen: int (struct tdb_context *)
+tdb_reopen_all: int (int)
+tdb_repack: int (struct tdb_context *)
+tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *)
+tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *)
+tdb_set_max_dead: void (struct tdb_context *, int)
+tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int)
+_tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_cancel: int (struct tdb_context *)
+tdb_transaction_commit: int (struct tdb_context *)
+tdb_transaction_lock: int (struct tdb_context *, int)
+tdb_transaction_prepare_commit: int (struct tdb_context *)
+tdb_transaction_recover: int (struct tdb_context *)
+tdb_transaction_start: int (struct tdb_context *)
+tdb_transaction_unlock: int (struct tdb_context *)
+tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *)
+tdb_unlock: int (struct tdb_context *, int, int)
+tdb_unlockall: int (struct tdb_context *)
+tdb_unlockall_read: int (struct tdb_context *)
+tdb_unlock_record: int (struct tdb_context *, tdb_off_t)
+tdb_validate_freelist: int (struct tdb_context *, int *)
+tdb_wipe_all: int (struct tdb_context *)
+tdb_write_lock_record: int (struct tdb_context *, tdb_off_t)
+tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t)
+tolower_sp: uint32_t (uint32_t)
+tolower_w: uint16_t (uint16_t)
+toupper_sp: uint32_t (uint32_t)
+toupper_w: uint16_t (uint16_t)
+type_configs: {{set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}, {set = false, syslog = false, fd = -1, level = log_none, display_options = 0}}
+ucs2_to_charset: size_t (charset_t, const uint16_t *, char *, size_t)
+ucs2_to_charset_allocate: size_t (charset_t, char **, const uint16_t *)
+unbecome_root: void (void)
+unix_rename: int (int, const char *, int, const char *)
+unix_strlower: size_t (const char *, size_t, char *, size_t)
+unix_strupper: size_t (const char *, size_t, char *, size_t)
+unload_volumes: void (AFPObj *)
+utf8_charlen: size_t (char *)
+utf8_decompose: size_t (char *, size_t, char *, size_t)
+utf8_precompose: size_t (char *, size_t, char *, size_t)
+utf8_strlen_validate: size_t (char *)
+utf8_strlower: size_t (const char *, size_t, char *, size_t)
+utf8_strupper: size_t (const char *, size_t, char *, size_t)
+utf8_to_charset_allocate: size_t (charset_t, char **, const char *)
+uuid_bin2string: const char *(const unsigned char *)
+uuidcache_dump: void (void)
+uuid_string2bin: void (const char *, unsigned char *)
+uuidtype: {"", "USER", "GROUP", "LOCAL"}
+volume_free: void (struct vol *)
+volume_unlink: void (struct vol *)
+writet: ssize_t (int, void *, const size_t, int, int)
index d549715f831b414fc098b334a97f6572c74ffc76..88ff7accd4d800287cb48828f6ad93ddbc83b98d 100644 (file)
@@ -25,7 +25,6 @@
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
-
 #include "tdb_private.h"
 
 /* check for an out of bounds access - if it is out of bounds then
@@ -212,7 +211,7 @@ void tdb_mmap(struct tdb_context *tdb)
        if (!(tdb->flags & TDB_NOMMAP)) {
                tdb->map_ptr = mmap(NULL, tdb->map_size, 
                                    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
-                                   MAP_SHARED|MAP_FILE, tdb->fd, 0);
+                                   MAP_SHARED, tdb->fd, 0);
 
                /*
                 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
index 41389c41c97a349acf69d8f1239751295546a518..de5dd02b141f3ca5cae4115938f8195aaeea4fc1 100644 (file)
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
-#if 0
-#include "replace.h"
-#include "system/filesys.h"
-#include "system/time.h"
-#include "system/shmem.h"
-#include "system/select.h"
-#include "system/wait.h"
-#include "tdb.h"
-#endif
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
 
 #include <unistd.h>
 #include <stdint.h>
@@ -50,6 +44,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <sys/param.h>
+#include <stddef.h>
 
 #ifndef __STRING
 #define __STRING(x)    #x
 typedef uint32_t tdb_len_t;
 typedef uint32_t tdb_off_t;
 
-#ifndef offsetof
-#define offsetof(t,f) ((unsigned int)&((t *)0)->f)
-#endif
-
 #define TDB_MAGIC_FOOD "TDB file\n"
 #define TDB_VERSION (0x26011967 + 6)
 #define TDB_MAGIC (0x26011999U)
index 785e47720f1d7ab2e74ce2be611c93164386fdb9..4e772e165a16e4f60f0f7dd331d176bf542645da 100644 (file)
@@ -95,6 +95,16 @@ int set_charset_name(charset_t ch, const char *name)
     return 0;
 }
 
+void free_charset_names(void)
+{
+    for (int ch = 0; ch < MAX_CHARSETS; ch++) {
+        if (charset_names[ch]) {
+            free(charset_names[ch]);
+            charset_names[ch] = NULL;
+        }
+    }
+}
+
 static struct charset_functions* get_charset_functions (charset_t ch)
 {
     if (charsets[ch] != NULL)
@@ -693,7 +703,7 @@ char * debug_out ( char * seq, size_t len)
  *      for e.g. HFS cdroms.
  */
 
-static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
+static size_t pull_charset_flags (charset_t from_set, charset_t to_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
 {
     const uint16_t option = (flags ? *flags : 0);
     size_t i_len, o_len;
@@ -702,6 +712,7 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     char* outbuf = dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
+    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     if (srclen == (size_t)-1)
         srclen = strlen(src) + 1;
@@ -717,10 +728,30 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     i_len=srclen;
     o_len=destlen;
 
+    if ((option & CONV_ESCAPEDOTS) && i_len >= 2 && inbuf[0] == '.') {
+        if (o_len < 6) {
+            errno = E2BIG;
+            goto end;
+        }
+        ucs2_t ucs2 = ':';
+        memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+        ucs2 = '2';
+        memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        ucs2 = 'e';
+        memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        outbuf += 6;
+        o_len -= 6;
+        inbuf++;
+        i_len--;
+        *flags |= CONV_REQESCAPE;
+    }
+
     while (i_len > 0) {
         for (j = 0; j < i_len; ++j)
-            if (inbuf[j] == ':')
+            if (inbuf[j] == ':' || inbuf[j] == '/') {
+                escch = inbuf[j];
                 break;
+            }
         j = i_len - j;
         i_len -= j;
 
@@ -750,48 +781,108 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
         }
 
         if (j) {
-            /* we have a ':' */
+            /* we have a ':' or '/' */
             i_len = j, j = 0;
 
-            if ((option & CONV_UNESCAPEHEX)) {
-                /* treat it as a CAP hex encoded char */
-                char h[MAXPATHLEN];
-                size_t hlen = 0;
-
-                while (i_len >= 3 && inbuf[0] == ':' &&
-                       isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
-                    h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
-                    inbuf += 3;
-                    i_len -= 3;
-                }
-                if (hlen) {
-                    const char *h_buf = h;
-                    if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
-                        i_len += hlen * 3;
-                        inbuf -= hlen * 3;
-                        if (errno == EILSEQ && (option & CONV_IGNORE)) {
+            if (escch == ':') {
+                if ((option & CONV_UNESCAPEHEX)) {
+                    /* treat it as a CAP hex encoded char */
+                    char h[MAXPATHLEN];
+                    size_t hlen = 0;
+
+                    while (i_len >= 3 && inbuf[0] == ':' &&
+                           isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
+                        h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
+                        inbuf += 3;
+                        i_len -= 3;
+                    }
+                    if (hlen) {
+                        const char *h_buf = h;
+                        if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
+                            i_len += hlen * 3;
+                            inbuf -= hlen * 3;
+                            if (errno == EILSEQ && (option & CONV_IGNORE)) {
+                                *flags |= CONV_REQMANGLE;
+                                return destlen - o_len;
+                            }
+                            goto end;
+                        }
+                    } else {
+                        /* We have an invalid :xx sequence */
+                        errno = EILSEQ;
+                        if ((option & CONV_IGNORE)) {
                             *flags |= CONV_REQMANGLE;
                             return destlen - o_len;
                         }
                         goto end;
                     }
-                } else {
-                    /* We have an invalid :xx sequence */
-                    errno = EILSEQ;
-                    if ((option & CONV_IGNORE)) {
-                        *flags |= CONV_REQMANGLE;
-                        return destlen - o_len;
+                } else if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
                     }
-                    goto end;
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '3';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'a';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if (to_set == CH_UTF8_MAC || to_set == CH_MAC) {
+                    /* convert to a '/' */
+                    ucs2_t slash = 0x002f;
+                    memcpy(outbuf, &slash, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
                 }
             } else {
-                /* a ':' that we just convert to a '/' */
-                ucs2_t slash = 0x002f;
-                memcpy(outbuf, &slash, sizeof(ucs2_t));
-                outbuf += 2;
-                o_len -= 2;
-                inbuf++;
-                i_len--;
+                /* '/' */
+                if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
+                    }
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '2';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'f';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if ((from_set == CH_UTF8_MAC || from_set == CH_MAC)
+                           && (to_set != CH_UTF8_MAC  || to_set != CH_MAC)) {
+                    /* convert to ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as '/' */
+                    ucs2_t ucs2 = 0x002f;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                }
             }
         }
     }
@@ -824,7 +915,6 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     char* outbuf = (char*)dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
-    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     descriptor = conv_handles[CH_UCS2][to_set];
     descriptor_cap = conv_handles[CH_UCS2][cap_set];
@@ -837,42 +927,7 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     i_len=srclen;
     o_len=destlen;
 
-    if ((option & CONV_ESCAPEDOTS) &&
-        i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
-        if (o_len < 3) {
-            errno = E2BIG;
-            goto end;
-        }
-        *outbuf++ = ':';
-        *outbuf++ = '2';
-        *outbuf++ = 'e';
-        o_len -= 3;
-        inbuf += 2;
-        i_len -= 2;
-        *flags |= CONV_REQESCAPE;
-    }
-
     while (i_len >= 2) {
-        for (i = 0; i < i_len; i += 2) {
-            ucs2_t c = SVAL(inbuf, i);
-            switch (c) {
-            case 0x003a: /* 0x003a = ':' */
-                if ( ! (option & CONV_ALLOW_COLON)) {
-                    errno = EILSEQ;
-                    goto end;
-                }
-                escch = c;
-                j = i_len - i;
-                i_len = i;
-                break;
-            case 0x002f: /* 0x002f = '/' */
-                if (option & CONV_ALLOW_SLASH) break;
-                escch = c;
-                j = i_len - i;
-                i_len = i;
-                break;
-            }
-        }
         while (i_len > 0 &&
                atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
             if (errno == EILSEQ) {
@@ -921,57 +976,8 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
             }
             goto end;
         }
+    } /* while (i_len >= 2) */
 
-        if (j) {
-            /* we have a ':' or '/' */
-            i_len = j, j = 0;
-
-            if ((option & CONV_ESCAPEHEX)) {
-                /* CAP hex encode it */
-                if (o_len < 3) {
-                    errno = E2BIG;
-                    goto end;
-                }
-                switch (escch) {
-                case '/':
-                    *outbuf++ = ':';
-                    *outbuf++ = '2';
-                    *outbuf++ = 'f';
-                    break;
-                case ':':
-                    *outbuf++ = ':';
-                    *outbuf++ = '3';
-                    *outbuf++ = 'a';
-                    break;
-                default:
-                    /*
-                     *  THIS SHOULD NEVER BE REACHED !!!
-                     *  As a safety net I put in a ' ' here
-                     */
-                    *outbuf++ = ':';
-                    *outbuf++ = '2';
-                    *outbuf++ = '0';
-                    break;
-                }
-                o_len -= 3;
-                inbuf += 2;
-                i_len -= 2;
-            } else {
-                switch (escch) {
-                case '/':
-                case ':':
-                    *outbuf++ = ':';
-                    break;
-                default: /* should never be reached */
-                    *outbuf++ = ' ';
-                    break;
-                }
-                o_len--;
-                inbuf += 2;
-                i_len -= 2;
-            }
-        }
-    }
     if (i_len > 0) errno = EINVAL;
 end:
     return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
@@ -991,7 +997,7 @@ size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_cha
     lazy_initialize_conv();
 
     /* convert from_set to UCS2 */
-    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len,
+    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, to_set, cap_charset, src, src_len,
                                                       (char *) buffer, sizeof(buffer) -2, flags)) ) {
         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
         return (size_t) -1;
index bf3a5bca9ef1e951b966a362f514e57954893458..5d2c72e6f0e2678f5b2e99b0ad36b6224c851616 100644 (file)
@@ -1,6 +1,5 @@
 
 /*
- * $Id: mac_roman.h,v 1.2 2005-04-28 20:50:04 bfernhomberg Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index ce3355df980c84c0f65e65f13f9b55ddd6556a86..0eba9a3c25faaa4ce255db5fdff95aaeaf1e315c 100644 (file)
@@ -291,12 +291,12 @@ int strcasecmp_w(const ucs2_t *a, const ucs2_t *b)
 
        while (*a && *b) {
                if ((0xD800 <= *a) && (*a < 0xDC00)) {
-                       if (ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1])) return ret;
+                       if ((ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1]))) return ret;
                        a++;
                        b++;
                        if (!(*a && *b)) return (tolower_w(*a) - tolower_w(*b)); /* avoid buffer over run */
                } else {
-                       if (ret = tolower_w(*a) - tolower_w(*b)) return ret;
+                       if ((ret = tolower_w(*a) - tolower_w(*b))) return ret;
                }
                a++;
                b++;
@@ -317,13 +317,13 @@ int strncasecmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
 
        while ((n < len) && *a && *b) {
                if ((0xD800 <= *a) && (*a < 0xDC00)) {
-                       if (ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1])) return ret;
+                       if ((ret = tolower_sp((uint32_t)*a << 16 | (uint32_t)a[1]) - tolower_sp((uint32_t)*b << 16 | (uint32_t)b[1]))) return ret;
                        a++;
                        b++;
                        n++;
                        if (!((n < len) && *a && *b)) return (tolower_w(*a) - tolower_w(*b));
                } else {
-                       if (ret = tolower_w(*a) - tolower_w(*b)) return ret;
+                       if ((ret = tolower_w(*a) - tolower_w(*b))) return ret;
                }
                a++;
                b++;
@@ -607,7 +607,7 @@ size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
                                base_sp = ((uint32_t)base << 16) | (uint32_t)comb;
                                do {
                                        comb_sp = ((uint32_t)in[1] << 16) | (uint32_t)in[2];
-                                       if (result_sp = do_precomposition_sp(base_sp, comb_sp)) {
+                                       if ((result_sp = do_precomposition_sp(base_sp, comb_sp))) {
                                                base_sp = result_sp;
                                                i += 4;
                                                in +=2;
@@ -641,7 +641,7 @@ size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
                }
 
                /* Binary Search for BMP */
-               else if (result = do_precomposition(base, comb)) {
+               else if ((result = do_precomposition(base, comb))) {
                        base = result;
                }
                
index 65ad1d6ca9704b8e535a1aa6e71d2d1b5847e7fd..201951664cd4d81b2da6445ca4ebeb866f99be8d 100644 (file)
@@ -9,6 +9,7 @@ libutil_la_SOURCES = \
        cnid.c          \
        fault.c         \
        getiface.c      \
+       gettok.c        \
        locking.c   \
        logger.c        \
        module.c        \
index 3a32c12e0293a35329a95bef956c5000646c6eb4..f0ac17f1b41be54dc9ee86c091a96799c6bc2d36 100644 (file)
@@ -45,7 +45,9 @@
 
 #include <atalk/ftw.h>
 
+#ifndef HAVE_MEMPCPY
 #define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
+#endif
 
 #define NDEBUG 1
 #include <assert.h>
diff --git a/libatalk/util/gettok.c b/libatalk/util/gettok.c
new file mode 100644 (file)
index 0000000..4859fec
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include <atalk/globals.h>
+
+static char    *l_curr;
+static char    *l_end;
+
+void initline( int len, char *line)
+{
+    l_curr = line;
+    l_end = line + len;
+}
+
+#define ST_QUOTE       0
+#define ST_WORD                1
+#define ST_BEGIN       2
+
+int
+parseline(int len, char *token)
+{
+    char       *p, *e;
+    int                state;
+
+    state = ST_BEGIN;
+    p = token;
+    e = token + len;
+
+    for (;;) {
+        if ( l_curr > l_end ) {                        /* end of line */
+            *token = '\0';
+            return( -1 );
+        }
+
+        switch ( *l_curr ) {
+        case '"' :
+            if ( state == ST_QUOTE ) {
+                state = ST_WORD;
+            } else {
+                state = ST_QUOTE;
+            }
+            break;
+
+        case '\0' :
+        case '\t' :
+        case '\n' :
+        case ' ' :
+            if ( state == ST_WORD ) {
+                *p = '\0';
+                return( p - token );
+            }
+            if ( state != ST_QUOTE ) {
+                break;
+            }
+            /* FALL THROUGH */
+
+        default :
+            if ( state == ST_BEGIN ) {
+                state = ST_WORD;
+            }
+            if ( p > e ) {                     /* end of token */
+                *token = '\0';
+                return( -1 );
+            }
+            *p++ = *l_curr;
+            break;
+        }
+
+        l_curr++;
+    }
+}
+
+#ifdef notdef
+void parseline(char *token, char *user)
+{
+    char               *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
+    struct passwd      *pwent;
+    int                        quoted = 0;
+
+    while ( isspace( *p )) {
+        p++;
+    }
+
+    /*
+     * If we've reached the end of the line, or a comment,
+     * don't return any more tokens.
+     */
+    if ( *p == '\0' || *p == '#' ) {
+        *token = '\0';
+        return;
+    }
+
+    if ( *p == '"' ) {
+        p++;
+        quoted = 1;
+    }
+    while ( *p != '\0' && ( quoted || !isspace( *p ))) {
+        if ( *p == '"' ) {
+            if ( quoted ) {
+                *t = '\0';
+                break;
+            }
+            quoted = 1;
+            p++;
+        } else {
+            *t++ = *p++;
+        }
+    }
+    pos = p;
+    *t = '\0';
+
+    /*
+     * We got to the end of the line without closing an open quote
+     */
+    if ( *p == '\0' && quoted ) {
+        *token = '\0';
+        return;
+    }
+
+    t = token;
+    if ( *t == '~' ) {
+        t++;
+        if ( *t == '\0' || *t == '/' ) {
+            u = user;
+            if ( *t == '/' ) {
+                t++;
+            }
+        } else {
+            u = t;
+            if (( q = strchr( t, '/' )) == NULL ) {
+                t = "";
+            } else {
+                *q = '\0';
+                t = q + 1;
+            }
+        }
+        if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
+            *token = '\0';
+            return;
+        }
+        strcpy( buf, pwent->pw_dir );
+        if ( *t != '\0' ) {
+            strcat( buf, "/" );
+            strcat( buf, t );
+        }
+        strcpy( token, buf );
+    }
+    return;
+}
+#endif /* notdef */
index 2d5b913e8026bb8e3121d45efba5d7f5e7e1ea42..0b473b608ba38cf4534b13fc513f34390123b1d7 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: locking.c,v 1.4 2010-01-05 19:05:52 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
index c8a2a0a8acd2845c43bfbd088e81b7a496855a1b..944234266eb4dc716144d5b1dc3f74021c341e49 100644 (file)
@@ -59,10 +59,10 @@ Netatalk 2001 (c)
   "CNID",                            \
   "AFPDaemon",                       \
   "DSI",                             \
-  "ATalkDaemon",                     \
-  "PAPDaemon",                       \
   "UAMS",                            \
-  "end_of_list_marker"}              \
+  "FCE",                             \
+  "ad",                              \
+  "end_of_list_marker"}
 
 /* =========================================================================
    Config
@@ -85,9 +85,9 @@ UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = {
     DEFAULT_LOG_CONFIG, /* logtype_cnid */
     DEFAULT_LOG_CONFIG, /* logtype_afpd */
     DEFAULT_LOG_CONFIG, /* logtype_dsi */
-    DEFAULT_LOG_CONFIG, /* logtype_atalkd */
-    DEFAULT_LOG_CONFIG, /* logtype_papd */
-    DEFAULT_LOG_CONFIG /* logtype_uams */
+    DEFAULT_LOG_CONFIG, /* logtype_uams */
+    DEFAULT_LOG_CONFIG,  /* logtype_fce */
+    DEFAULT_LOG_CONFIG  /* logtype_ad */
 };
 
 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility);
@@ -594,11 +594,8 @@ log:
                                  loglevel, logtype);
 
         /* If default wasnt setup its fd is -1 */
-        iov[0].iov_base = log_details_buffer;
-        iov[0].iov_len = strlen(log_details_buffer);
-        iov[1].iov_base = temp_buffer;
-        iov[1].iov_len = strlen(temp_buffer);
-        writev( fd,  iov, 2);
+        write(fd, log_details_buffer, strlen(log_details_buffer));
+        write(fd, temp_buffer, strlen(temp_buffer));
     } else {
         write(fd, temp_buffer, strlen(temp_buffer));
     }
index 1e370373b3f810a7de13ce96b8f72b0f4d50ea0b..b651476524c73230cb3f3875fd8288bd52e86ec2 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: module.c,v 1.5 2003-02-17 02:03:12 srittau Exp $
  */
 
 #ifdef HAVE_CONFIG_H
index 0c0a9416f0f60bb4c4948ee64f589d9ca5744ffc..923e9c84357ffdca3f42e8350467dee6f3aa52a1 100644 (file)
@@ -51,6 +51,7 @@
 #include <atalk/uuid.h>
 #include <atalk/netatalk_conf.h>
 #include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #define VOLPASSLEN  8
 #ifndef UUID_PRINTABLE_STRING_LENGTH
@@ -401,8 +402,11 @@ static char *volxlate(const AFPObj *obj,
 /*!
  * check access list
  *
- * this function wants something of the following form:
- * "@group,name,name2,@group2,name3" or "@group name name2 @group2 name3"
+ * this function wants a string consisting of names seperated by comma
+ * or space. Names may be quoted within a pair of quotes. Groups are
+ * denoted by a leading @ symbol.
+ * Example:
+ * user1 user2, user3, @group1 @group2, @group3 "user name1", "@group name1"
  * A NULL argument allows everybody to have access.
  * We return three things:
  *     -1: no list
@@ -411,26 +415,31 @@ static char *volxlate(const AFPObj *obj,
  */
 static int accessvol(const AFPObj *obj, const char *args, const char *name)
 {
-    char buf[MAXPATHLEN + 1], *p;
+    EC_INIT;
+    char *names = NULL, *p;
     struct group *gr;
 
     if (!args)
-        return -1;
+        EC_EXIT_STATUS(-1);
 
-    strlcpy(buf, args, sizeof(buf));
-    if ((p = strtok(buf, ", ")) == NULL) /* nothing, return okay */
-        return -1;
+    EC_NULL_LOG( names = strdup(args) );
+
+    if ((p = strtok_quote(names, ", ")) == NULL) /* nothing, return okay */
+        EC_EXIT_STATUS(-1);
 
     while (p) {
         if (*p == '@') { /* it's a group */
             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid, obj->ngroups, obj->groups))
-                return 1;
+                EC_EXIT_STATUS(1);
         } else if (strcasecmp(p, name) == 0) /* it's a user name */
-            return 1;
-        p = strtok(NULL, ", ");
+            EC_EXIT_STATUS(1);
+        p = strtok_quote(NULL, ", ");
     }
 
-    return 0;
+EC_CLEANUP:
+    if (names)
+        free(names);
+    EC_EXIT;
 }
 
 static int hostaccessvol(const AFPObj *obj, const char *volname, const char *args)
@@ -505,13 +514,11 @@ static int hostaccessvol(const AFPObj *obj, const char *volname, const char *arg
  */
 static const char *getoption(const dictionary *conf, const char *vol, const char *opt, const char *defsec, const char *defval)
 {
-    EC_INIT;
     const char *result;
 
     if ((!(result = iniparser_getstring(conf, vol, opt, NULL))) && (defsec != NULL))
         result = iniparser_getstring(conf, defsec, opt, NULL);
     
-EC_CLEANUP:
     if (result == NULL)
         result = defval;
     return result;
@@ -530,13 +537,11 @@ EC_CLEANUP:
  */
 static int getoption_bool(const dictionary *conf, const char *vol, const char *opt, const char *defsec, int defval)
 {
-    EC_INIT;
     int result;
 
     if (((result = iniparser_getboolean(conf, vol, opt, -1)) == -1) && (defsec != NULL))
         result = iniparser_getboolean(conf, defsec, opt, -1);
     
-EC_CLEANUP:
     if (result == -1)
         result = defval;
     return result;
@@ -549,7 +554,7 @@ EC_CLEANUP:
  * @param pwd      (r) struct passwd of logged in user, may be NULL in master afpd
  * @param section  (r) volume name wo variables expanded (exactly as in iniconfig)
  * @param name     (r) volume name
- * @param path     (r) volume path
+ * @param path_in  (r) volume path
  * @param preset   (r) default preset, may be NULL
  * @returns            vol on success, NULL on error
  */
@@ -557,40 +562,55 @@ static struct vol *creatvol(AFPObj *obj,
                             const struct passwd *pwd,
                             const char *section,
                             const char *name,
-                            const char *path,
+                            const char *path_in,
                             const char *preset)
 {
     EC_INIT;
     struct vol  *volume = NULL;
     int         i, suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
-    char        *tmpname;
+    char        tmpname[AFPVOL_U8MNAMELEN+1];
+    char        path[MAXPATHLEN + 1];
     ucs2_t      u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
     char        suffix[6]; /* max is #FFFF */
     uint16_t    flags;
     const char  *val;
     char        *p, *q;
 
+    strlcpy(path, path_in, MAXPATHLEN);
+
     LOG(log_debug, logtype_afpd, "createvol(volume: '%s', path: \"%s\", preset: '%s'): BEGIN",
         name, path, preset ? preset : "-");
 
     if ( name == NULL || *name == '\0' ) {
-        if ((name = strrchr( path, '/' )) == NULL) {
+        if ((name = strrchr( path, '/' )) == NULL)
             EC_FAIL;
-        }
-
         /* if you wish to share /, you need to specify a name. */
         if (*++name == '\0')
             EC_FAIL;
     }
 
     /* Once volumes are loaded, we never change options again, we just delete em when they're removed from afp.conf */
+
     for (struct vol *vol = Volumes; vol; vol = vol->v_next) {
-        if (STRCMP(path, ==, vol->v_path)) {
-            LOG(log_debug, logtype_afpd, "createvol('%s'): already loaded", name);
+        if (STRCMP(name, ==, vol->v_localname) && vol->v_deleted) {
+            /* 
+             * reloading config, volume still present, nothing else to do,
+             * we don't change options for volumes once they're loaded
+             */
             vol->v_deleted = 0;
             volume = vol;
-            goto EC_CLEANUP;
+            EC_EXIT_STATUS(0);
         }
+        if (STRCMP(path, ==, vol->v_path)) {
+            LOG(log_note, logtype_afpd, "volume \"%s\" path \"%s\" is the same as volumes \"%s\" path",
+                name, path, vol->v_configname);
+            EC_EXIT_STATUS(0);
+        }
+        /*
+         * We could check for nested volume paths here, but
+         * nobody was able to come up with an implementation yet,
+         * that is simple, fast and correct.
+         */
     }
 
     /*
@@ -616,14 +636,14 @@ static struct vol *creatvol(AFPObj *obj,
     volume->v_vfs_ea = AFPVOL_EA_AUTO;
     volume->v_umask = obj->options.umask;
 
-    if (val = getoption(obj->iniconfig, section, "password", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "password", preset, NULL)))
         EC_NULL( volume->v_password = strdup(val) );
 
-    if (val = getoption(obj->iniconfig, section, "veto files", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "veto files", preset, NULL)))
         EC_NULL( volume->v_veto = strdup(val) );
 
     /* vol charset is in [G] and [V] */
-    if (val = getoption(obj->iniconfig, section, "vol charset", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "vol charset", preset, NULL))) {
         if (strcasecmp(val, "UTF-8") == 0) {
             val = strdup("UTF8");
         }
@@ -633,7 +653,7 @@ static struct vol *creatvol(AFPObj *obj,
         EC_NULL( volume->v_volcodepage = strdup(obj->options.volcodepage) );
 
     /* mac charset is in [G] and [V] */
-    if (val = getoption(obj->iniconfig, section, "mac charset", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "mac charset", preset, NULL))) {
         if (strncasecmp(val, "MAC", 3) != 0) {
             LOG(log_warning, logtype_afpd, "Is '%s' really mac charset? ", val);
         }
@@ -643,46 +663,46 @@ static struct vol *creatvol(AFPObj *obj,
     EC_NULL( volume->v_maccodepage = strdup(obj->options.maccodepage) );
 
     vlen = strlen(name);
-    tmpname = strdup(name);
+    strlcpy(tmpname, name, sizeof(tmpname));
     for(i = 0; i < vlen; i++)
         if(tmpname[i] == '/') tmpname[i] = ':';
 
     bstring dbpath;
-    EC_NULL_LOG( val = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
-    EC_NULL_LOG( dbpath = bformat("%s/%s/", val, tmpname) );
-    EC_NULL_LOG( volume->v_dbpath = strdup(bdata(dbpath)) );
+    EC_NULL( val = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
+    EC_NULL( dbpath = bformat("%s/%s/", val, tmpname) );
+    EC_NULL( volume->v_dbpath = strdup(cfrombstr(dbpath)) );
     bdestroy(dbpath);
 
-    if (val = getoption(obj->iniconfig, section, "cnid scheme", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "cnid scheme", preset, NULL)))
         EC_NULL( volume->v_cnidscheme = strdup(val) );
     else
         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
 
-    if (val = getoption(obj->iniconfig, section, "umask", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "umask", preset, NULL)))
         volume->v_umask = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "directory perm", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "directory perm", preset, NULL)))
         volume->v_dperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "file perm", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "file perm", preset, NULL)))
         volume->v_fperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, section, "vol size limit", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "vol size limit", preset, NULL)))
         volume->v_limitsize = (uint32_t)strtoul(val, NULL, 10);
 
-    if (val = getoption(obj->iniconfig, section, "preexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "preexec", preset, NULL)))
         EC_NULL( volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "postexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "postexec", preset, NULL)))
         EC_NULL( volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "root preexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "root preexec", preset, NULL)))
         EC_NULL( volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "root postexec", preset, NULL))
+    if ((val = getoption(obj->iniconfig, section, "root postexec", preset, NULL)))
         EC_NULL( volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, section, "appledouble", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "appledouble", preset, NULL))) {
         if (strcmp(val, "v2") == 0)
             volume->v_adouble = AD_VERSION2;
         else if (strcmp(val, "ea") == 0)
@@ -691,10 +711,10 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_adouble = AD_VERSION;
     }
 
-    if (val = getoption(obj->iniconfig, section, "cnid server", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "cnid server", preset, NULL))) {
         EC_NULL( p = strdup(val) );
         volume->v_cnidserver = p;
-        if (q = strrchr(val, ':')) {
+        if ((q = strrchr(val, ':'))) {
             *q++ = 0;
             volume->v_cnidport = strdup(q);
         } else {
@@ -706,7 +726,7 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_cnidport = strdup(obj->options.Cnid_port);
     }
 
-    if (val = getoption(obj->iniconfig, section, "ea", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "ea", preset, NULL))) {
         if (strcasecmp(val, "ad") == 0)
             volume->v_vfs_ea = AFPVOL_EA_AD;
         else if (strcasecmp(val, "sys") == 0)
@@ -715,7 +735,7 @@ static struct vol *creatvol(AFPObj *obj,
             volume->v_vfs_ea = AFPVOL_EA_NONE;
     }
 
-    if (val = getoption(obj->iniconfig, section, "casefold", preset, NULL)) {
+    if ((val = getoption(obj->iniconfig, section, "casefold", preset, NULL))) {
         if (strcasecmp(val, "tolower") == 0)
             volume->v_casefold = AFPVOL_UMLOWER;
         else if (strcasecmp(val, "toupper") == 0)
@@ -750,6 +770,8 @@ static struct vol *creatvol(AFPObj *obj,
 #endif
     if (!getoption_bool(obj->iniconfig, section, "convert appledouble", preset, 1))
         volume->v_flags |= AFPVOL_NOV2TOEACONV;
+    if (getoption_bool(obj->iniconfig, section, "follow symlinks", preset, 0))
+        volume->v_flags |= AFPVOL_FOLLOWSYM;
 
     if (getoption_bool(obj->iniconfig, section, "preexec close", preset, 0))
         volume->v_preexec_close = 1;
@@ -777,6 +799,10 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_ad_options |= ADVOL_UNIXPRIV;
     if ((volume->v_flags & AFPVOL_INV_DOTS))
         volume->v_ad_options |= ADVOL_INVDOTS;
+    if ((volume->v_flags & AFPVOL_FOLLOWSYM))
+        volume->v_ad_options |= ADVOL_FOLLO_SYML;
+    if ((volume->v_flags & AFPVOL_RO))
+        volume->v_ad_options |= ADVOL_RO;
 
     /* Mac to Unix conversion flags*/
     if ((volume->v_flags & AFPVOL_EILSEQ))
@@ -802,7 +828,7 @@ static struct vol *creatvol(AFPObj *obj,
 
     /* Unicode Volume Name */
     /* Firstly convert name from unixcharset to UTF8-MAC */
-    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
+    flags = CONV_IGNORE;
     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -812,7 +838,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
         if (tmpvlen + suffixlen > obj->options.volnamelen) {
-            flags = CONV_FORCE | CONV_ALLOW_SLASH;
+            flags = CONV_FORCE;
             tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
         }
@@ -828,7 +854,7 @@ static struct vol *creatvol(AFPObj *obj,
 
     /* Maccharset Volume Name */
     /* Firsty convert name from unixcharset to maccharset */
-    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
+    flags = CONV_IGNORE;
     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -838,7 +864,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
-            flags = CONV_FORCE | CONV_ALLOW_SLASH;
+            flags = CONV_FORCE;
             tmpvlen = convert_charset(obj->options.unixcharset,
                                       obj->options.maccharset,
                                       0,
@@ -867,10 +893,9 @@ static struct vol *creatvol(AFPObj *obj,
     EC_NULL( volume->v_localname = strdup(name) );
     EC_NULL( volume->v_u8mname = strdup_w(u8mtmpname) );
     EC_NULL( volume->v_macname = strdup_w(mactmpname) );
-    EC_NULL( volume->v_path = malloc(strlen(path) + 1) );
-
+    EC_NULL( volume->v_path = strdup(path) ); 
+        
     volume->v_name = utf8_encoding(obj) ? volume->v_u8mname : volume->v_macname;
-    strcpy(volume->v_path, path);
 
 #ifdef __svr4__
     volume->v_qfd = -1;
@@ -913,10 +938,8 @@ static struct vol *creatvol(AFPObj *obj,
 EC_CLEANUP:
     LOG(log_debug, logtype_afpd, "createvol: END: %d", ret);
     if (ret != 0) {
-        if (volume) {
+        if (volume)
             volume_free(volume);
-            free(volume);
-        }
         return NULL;
     }
     return volume;
@@ -953,13 +976,11 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
     EC_INIT;
     static int regexerr = -1;
     static regex_t reg;
-    char        path[MAXPATHLEN + 1];
+    char        *realvolpath;
     char        volname[AFPVOL_U8MNAMELEN + 1];
-    char        tmp[MAXPATHLEN + 1];
+    char        path[MAXPATHLEN + 1], tmp[MAXPATHLEN + 1];
     const char  *preset, *default_preset, *p, *basedir;
-    char        *q, *u;
     int         i;
-    struct passwd   *pw;
     regmatch_t match[1];
 
     LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
@@ -987,6 +1008,9 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 /* no user home */
                 continue;
 
+            if ((realpath(pwent->pw_dir, tmp)) == NULL)
+                continue;
+
             /* check if user home matches our "basedir regex" */
             if ((basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
                 LOG(log_error, logtype_afpd, "\"basedir regex =\" must be defined in [Homes] section");
@@ -998,18 +1022,19 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 char errbuf[1024];
                 regerror(regexerr, &reg, errbuf, sizeof(errbuf));
                 LOG(log_debug, logtype_default, "readvolfile: bad basedir regex: %s", errbuf);
+                continue;
             }
 
-            if (regexec(&reg, pwent->pw_dir, 1, match, 0) == REG_NOMATCH) {
-                LOG(log_debug, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
-                    pwent->pw_dir, basedir);
+            if (regexec(&reg, tmp, 1, match, 0) == REG_NOMATCH) {
+                LOG(log_error, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
+                    tmp, basedir);
                 continue;
             }
 
-            strlcpy(tmp, pwent->pw_dir, MAXPATHLEN);
-            strlcat(tmp, "/", MAXPATHLEN);
-            if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
+            if ((p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))) {
+                strlcat(tmp, "/", MAXPATHLEN);
                 strlcat(tmp, p, MAXPATHLEN);
+            }
         } else {
             /* Get path */
             if ((p = iniparser_getstring(obj->iniconfig, secname, "path", NULL)) == NULL)
@@ -1040,13 +1065,170 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
 
         preset = iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
 
-        creatvol(obj, pwent, secname, volname, path, preset ? preset : default_preset ? default_preset : NULL);
+        if ((realvolpath = realpath_safe(path)) == NULL)
+            continue;
+
+        creatvol(obj, pwent, secname, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
+        free(realvolpath);
+    }
+
+// EC_CLEANUP:
+    EC_EXIT;
+}
+
+static struct extmap    *Extmap = NULL, *Defextmap = NULL;
+static int              Extmap_cnt;
+
+static int setextmap(char *ext, char *type, char *creator)
+{
+    EC_INIT;
+    struct extmap *em;
+    int           cnt;
+
+    if (Extmap == NULL) {
+        EC_NULL_LOG( Extmap = calloc(1, sizeof( struct extmap )) );
+    }
+
+    ext++;
+
+    for (em = Extmap, cnt = 0; em->em_ext; em++, cnt++)
+        if ((strdiacasecmp(em->em_ext, ext)) == 0)
+            goto EC_CLEANUP;
+
+    EC_NULL_LOG( Extmap = realloc(Extmap, sizeof(struct extmap) * (cnt + 2)) );
+    (Extmap + cnt + 1)->em_ext = NULL;
+    em = Extmap + cnt;
+
+    EC_NULL( em->em_ext = strdup(ext) );
+
+    if ( *type == '\0' ) {
+        memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type ));
+    } else {
+        memcpy(em->em_type, type, sizeof( em->em_type ));
+    }
+    if ( *creator == '\0' ) {
+        memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator ));
+    } else {
+        memcpy(em->em_creator, creator, sizeof( em->em_creator ));
     }
 
 EC_CLEANUP:
     EC_EXIT;
 }
 
+/* -------------------------- */
+static int extmap_cmp(const void *map1, const void *map2)
+{
+    const struct extmap *em1 = map1;
+    const struct extmap *em2 = map2;
+    return strdiacasecmp(em1->em_ext, em2->em_ext);
+}
+
+static void sortextmap( void)
+{
+    struct extmap   *em;
+
+    Extmap_cnt = 0;
+    if ((em = Extmap) == NULL) {
+        return;
+    }
+    while (em->em_ext) {
+        em++;
+        Extmap_cnt++;
+    }
+    if (Extmap_cnt) {
+        qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp);
+        if (*Extmap->em_ext == 0) {
+            /* the first line is really "." the default entry,
+             * we remove the leading '.' in setextmap
+             */
+            Defextmap = Extmap;
+        }
+    }
+}
+
+static void free_extmap( void)
+{
+    struct extmap   *em;
+
+    if (Extmap) {
+        for ( em = Extmap; em->em_ext; em++) {
+            free (em->em_ext);
+        }
+        free(Extmap);
+        Extmap = NULL;
+        Defextmap = Extmap;
+        Extmap_cnt = 0;
+    }
+}
+
+static int ext_cmp_key(const void *key, const void *obj)
+{
+    const char          *p = key;
+    const struct extmap *em = obj;
+    return strdiacasecmp(p, em->em_ext);
+}
+
+struct extmap *getextmap(const char *path)
+{
+    char      *p;
+    struct extmap *em;
+
+    if (!Extmap_cnt || NULL == ( p = strrchr( path, '.' )) ) {
+        return( Defextmap );
+    }
+    p++;
+    if (!*p) {
+        return( Defextmap );
+    }
+    em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
+    if (em) {
+        return( em );
+    } else {
+        return( Defextmap );
+    }
+}
+
+struct extmap *getdefextmap(void)
+{
+    return( Defextmap );
+}
+
+static int readextmap(const char *file)
+{
+    EC_INIT;
+    FILE        *fp;
+    char        ext[256];
+    char        buf[256];
+    char        type[5], creator[5];
+
+    LOG(log_debug, logtype_afpd, "readextmap: loading \"%s\"", file);
+
+    EC_NULL_LOGSTR( fp = fopen(file, "r"), "Couldn't open extension maping file %s", file);
+
+    while (fgets(buf, sizeof(buf), fp) != NULL) {
+        initline(strlen(buf), buf);
+        parseline(sizeof(ext) - 1, ext);
+
+        switch (ext[0]) {
+        case '.' :
+            parseline(sizeof(type) - 1, type);
+            parseline(sizeof(creator) - 1, creator);
+            setextmap(ext, type, creator);
+            LOG(log_debug, logtype_afpd, "readextmap: mapping: '%s' -> %s/%s", ext, type, creator);
+            break;
+        }
+    }
+
+    sortextmap();
+    EC_ZERO( fclose(fp) );
+
+    LOG(log_debug, logtype_afpd, "readextmap: done", file);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
 /**************************************************************
  * API functions
  **************************************************************/
@@ -1076,12 +1258,14 @@ void volume_unlink(struct vol *volume)
 }
 
 /*!
- * Free all resources allocated in a struct vol, only struct dir *v_root can't be freed
+ * Free all resources allocated in a struct vol in load_volumes()
+ *
+ * Actually opening a volume (afp_openvol()) will allocate additional
+ * ressources which are freed in closevol()
  */
 void volume_free(struct vol *vol)
 {
-    LOG(log_debug, logtype_afpd, "volume_free('%s'): BEGIN", vol->v_localname);
-
+    free(vol->v_configname);
     free(vol->v_localname);
     free(vol->v_u8mname);
     free(vol->v_macname);
@@ -1096,10 +1280,12 @@ void volume_free(struct vol *vol)
     free(vol->v_uuid);
     free(vol->v_cnidserver);
     free(vol->v_cnidport);
+    free(vol->v_preexec);
     free(vol->v_root_preexec);
     free(vol->v_postexec);
+    free(vol->v_root_postexec);
 
-    LOG(log_debug, logtype_afpd, "volume_free: END");
+    free(vol);
 }
 
 /*!
@@ -1128,7 +1314,7 @@ int load_charset(struct vol *vol)
  * @param obj       (r) handle
  * @param delvol_fn (r) callback called for deleted volumes
  */
-int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *))
+int load_volumes(AFPObj *obj)
 {
     EC_INIT;
     int fd = -1;
@@ -1139,6 +1325,9 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
 
     LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
 
+    if (obj->uid)
+        pwent = getpwuid(obj->uid);
+
     if (Volumes) {
         if (!volfile_changed(&obj->options))
             goto EC_CLEANUP;
@@ -1146,6 +1335,15 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
         for (vol = Volumes; vol; vol = vol->v_next) {
             vol->v_deleted = 1;
         }
+        if (obj->uid) {
+            become_root();
+            ret = set_groups(obj, pwent);
+            unbecome_root();
+            if (ret != 0) {
+                LOG(log_error, logtype_afpd, "load_volumes: set_groups: %s", strerror(errno));
+                EC_FAIL;
+            }
+        }
     } else {
         LOG(log_debug, logtype_afpd, "load_volumes: no volumes yet");
         EC_ZERO_LOG( lstat(obj->options.configfile, &st) );
@@ -1170,9 +1368,6 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
         break;
     }
 
-    if (obj->uid)
-        pwent = getpwuid(obj->uid);
-
     if (obj->iniconfig)
         iniparser_freedict(obj->iniconfig);
     LOG(log_debug, logtype_afpd, "load_volumes: loading: %s", obj->options.configfile);
@@ -1180,12 +1375,24 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(const AFPObj *obj, struct vol *)
 
     EC_ZERO_LOG( readvolfile(obj, pwent) );
 
-    for ( vol = Volumes; vol; vol = vol->v_next ) {
-        if (vol->v_deleted) {
+    struct vol *p, *prevvol;
+
+    vol = Volumes;
+    prevvol = NULL;
+
+    while (vol) {
+        if (vol->v_deleted && !(vol->v_flags & AFPVOL_OPEN)) {
             LOG(log_debug, logtype_afpd, "load_volumes: deleted: %s", vol->v_localname);
-            if (delvol_fn)
-                delvol_fn(obj, vol);
-            vol = Volumes;
+            if (prevvol)
+                prevvol->v_next = vol->v_next;
+            else
+                Volumes = NULL;
+            p = vol->v_next;
+            volume_free(vol);
+            vol = p;
+        } else {
+            prevvol = vol;
+            vol = vol->v_next;
         }
     }
 
@@ -1199,12 +1406,16 @@ EC_CLEANUP:
 
 void unload_volumes(AFPObj *obj)
 {
-    struct vol *vol;
+    struct vol *vol, *p;
 
     LOG(log_debug, logtype_afpd, "unload_volumes: BEGIN");
 
-    for (vol = Volumes; vol; vol = vol->v_next)
+    p = Volumes;
+    while (p) {
+        vol = p;
+        p = vol->v_next;
         volume_free(vol);
+    }
     Volumes = NULL;
     obj->options.volfile.mtime = 0;
     
@@ -1232,6 +1443,56 @@ struct vol *getvolbyvid(const uint16_t vid )
     return( vol );
 }
 
+/*
+ * get username by path
+ * 
+ * getvolbypath() assumes that the user home directory has the same name as the username.
+ * If that is not true, getuserbypath() is called and tries to retrieve the username
+ * from the directory owner, checking its validity.
+ * 
+ * @param   path (r) absolute volume path
+ * @returns NULL     if no match is found, pointer to username if successfull
+ *
+ */ 
+static char *getuserbypath(const char *path)
+{
+    EC_INIT;
+    struct stat sbuf;
+    struct passwd  *pwd;
+    char *hdir = NULL;
+
+    LOG(log_debug, logtype_afpd, "getuserbypath(\"%s\")", path);
+
+    /* does folder exists? */
+    if (stat(path, &sbuf) != 0)
+        EC_FAIL;
+
+    /* get uid of dir owner */
+    if ((pwd = getpwuid(sbuf.st_uid)) == NULL)
+        EC_FAIL;
+
+    /* does user home directory exists? */
+    if (stat(pwd->pw_dir, &sbuf) != 0)
+        EC_FAIL;
+
+    /* resolve and remove symlinks */
+    if ((hdir = realpath_safe(pwd->pw_dir)) == NULL) 
+        EC_FAIL;
+
+    /* handle subdirectories, path = */
+    if (strncmp(path, hdir, strlen(hdir)) != 0)
+        EC_FAIL;
+
+    LOG(log_debug, logtype_afpd, "getuserbypath: match user: %s, home: %s, realhome: %s",
+        pwd->pw_name, pwd->pw_dir, hdir);
+
+EC_CLEANUP:
+    if (hdir)
+        free(hdir);
+    if (ret != 0)
+        return NULL;
+    return pwd->pw_name;
+}
 /*!
  * Search volume by path, creating user home vols as necessary
  *
@@ -1247,6 +1508,9 @@ struct vol *getvolbyvid(const uint16_t vid )
  * (3) If there is, match "path" with "basedir regex" to get the user home parent dir
  * (4) Built user home path by appending the basedir matched in (3) and appending the username
  * (5) The next path element then is the username
+ * (5b) getvolbypath() assumes that the user home directory has the same name as the username.
+ *     If that is not true, getuserbypath() is called and tries to retrieve the username
+ *     from the directory owner, checking its validity
  * (6) Append [Homes]->path subdirectory if defined
  * (7) Create volume
  *
@@ -1263,7 +1527,7 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     const struct passwd *pw;
     char        volname[AFPVOL_U8MNAMELEN + 1];
     char        abspath[MAXPATHLEN + 1];
-    char        volpath[MAXPATHLEN + 1];
+    char        volpath[MAXPATHLEN + 1], *realvolpath = NULL;
     char        tmpbuf[MAXPATHLEN + 1];
     const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
     char *user = NULL, *prw;
@@ -1333,20 +1597,30 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
         p++;
     EC_NULL_LOG( user = strdup(p) );
 
-    if (prw = strchr(user, '/'))
+    if ((prw = strchr(user, '/')))
         *prw++ = 0;
     if (prw != 0)
         subpath = prw;
 
     strlcat(tmpbuf, user, MAXPATHLEN);
+    if (getpwnam(user) == NULL) {
+        /* (5b) */
+        char *tuser;
+        if ((tuser = getuserbypath(tmpbuf)) != NULL) {
+            free(user);
+            user = strdup(tuser);
+        }
+    }
     strlcpy(obj->username, user, MAXUSERLEN);
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
     /* (6) */
-    if (subpathconfig = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL)) {
+    if ((subpathconfig = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))) {
+        /*
         if (!subpath || strncmp(subpathconfig, subpath, strlen(subpathconfig)) != 0) {
             EC_FAIL;
         }
+        */
         strlcat(tmpbuf, subpathconfig, MAXPATHLEN);
         strlcat(tmpbuf, "/", MAXPATHLEN);
     }
@@ -1354,29 +1628,32 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
 
     /* (7) */
     if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
-        return NULL;
+        EC_FAIL;
 
+    EC_NULL( realvolpath = realpath_safe(volpath) );
     EC_NULL( pw = getpwnam(user) );
 
-    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => volpath: \"%s\"",
-        path, user, pw->pw_dir, volpath);
+    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => realvolpath: \"%s\"",
+        path, user, pw->pw_dir, realvolpath);
 
     /* do variable substitution for volume name */
     p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
     if (strstr(p, "$u") == NULL)
         p = "$u's home";
     strlcpy(tmpbuf, p, AFPVOL_U8MNAMELEN);
-    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, volpath, NULL) );
+    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, realvolpath, NULL) );
 
     const char  *preset, *default_preset;
     default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL);
     preset = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "vol preset", NULL);
 
-    vol = creatvol(obj, pw, INISEC_HOMES, volname, volpath, preset ? preset : default_preset ? default_preset : NULL);
+    vol = creatvol(obj, pw, INISEC_HOMES, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
 
 EC_CLEANUP:
     if (user)
         free(user);
+    if (realvolpath)
+        free(realvolpath);
     if (ret != 0)
         vol = NULL;
     return vol;
@@ -1405,8 +1682,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     EC_INIT;
     dictionary *config;
     struct afp_options *options = &AFPObj->options;
-    int i, c;
-    const char *p, *tmp;
+    int c;
+    const char *p;
     char *q, *r;
     char val[MAXVAL];
 
@@ -1434,10 +1711,6 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->flags |= OPTION_NOZEROCONF;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "advertise ssh", 0))
         options->flags |= OPTION_ANNOUNCESSH;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "map acls", 1))
-        options->flags |= OPTION_ACL2MACCESS;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "keep sessions", 0))
-        options->flags |= OPTION_KEEPSESSIONS;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "close vol", 0))
         options->flags |= OPTION_CLOSEVOL;
     if (!iniparser_getboolean(config, INISEC_GLOBAL, "client polling", 0))
@@ -1446,6 +1719,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->flags |= OPTION_NOSENDFILE;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "solaris share reservations", 1))
         options->flags |= OPTION_SHARE_RESERV;
+    if (iniparser_getboolean(config, INISEC_GLOBAL, "afpstats", 0))
+        options->flags |= OPTION_DBUS_AFPSTATS;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
         options->flags |= OPTION_AFP_READ_LOCK;
     if (!iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
@@ -1456,7 +1731,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     /* figure out options w values */
     options->loginmesg      = iniparser_getstrdup(config, INISEC_GLOBAL, "login message",  NULL);
     options->guest          = iniparser_getstrdup(config, INISEC_GLOBAL, "guest account",  "nobody");
-    options->passwdfile     = iniparser_getstrdup(config, INISEC_GLOBAL, "passwd file",_PATH_AFPDPWFILE);
+    options->extmapfile     = iniparser_getstrdup(config, INISEC_GLOBAL, "extmap file",    _PATH_CONFDIR "extmap.conf");
+    options->passwdfile     = iniparser_getstrdup(config, INISEC_GLOBAL, "passwd file",    _PATH_AFPDPWFILE);
     options->uampath        = iniparser_getstrdup(config, INISEC_GLOBAL, "uam path",       _PATH_AFPDUAMPATH);
     options->uamlist        = iniparser_getstrdup(config, INISEC_GLOBAL, "uam list",       "uams_dhx.so uams_dhx2.so");
     options->port           = iniparser_getstrdup(config, INISEC_GLOBAL, "afp port",       "548");
@@ -1464,7 +1740,9 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->k5service      = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 service",     NULL);
     options->k5realm        = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 realm",       NULL);
     options->listen         = iniparser_getstrdup(config, INISEC_GLOBAL, "afp listen",     NULL);
+    options->interfaces     = iniparser_getstrdup(config, INISEC_GLOBAL, "afp interfaces", NULL);
     options->ntdomain       = iniparser_getstrdup(config, INISEC_GLOBAL, "nt domain",      NULL);
+    options->addomain       = iniparser_getstrdup(config, INISEC_GLOBAL, "ad domain",      NULL);
     options->ntseparator    = iniparser_getstrdup(config, INISEC_GLOBAL, "nt separator",   NULL);
     options->mimicmodel     = iniparser_getstrdup(config, INISEC_GLOBAL, "mimic model",    NULL);
     options->adminauthuser  = iniparser_getstrdup(config, INISEC_GLOBAL, "admin auth user",NULL);
@@ -1482,6 +1760,18 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->sleep          = iniparser_getint   (config, INISEC_GLOBAL, "sleep time",     10);
     options->disconnected   = iniparser_getint   (config, INISEC_GLOBAL, "disconnect time",24);
 
+    p = iniparser_getstring(config, INISEC_GLOBAL, "map acls", "rights");
+    if (STRCMP(p, ==, "rights"))
+        options->flags |= OPTION_ACL2MACCESS;
+    else if (STRCMP(p, ==, "mode"))
+        options->flags |= OPTION_ACL2MODE | OPTION_ACL2MACCESS;
+    else {
+        if (STRCMP(p, !=, "none")) {
+            LOG(log_error, logtype_afpd, "bad ACL mapping option: %s, defaulting to 'rights'", p);
+            options->flags |= OPTION_ACL2MACCESS;
+        }
+    }
+
     if ((p = iniparser_getstring(config, INISEC_GLOBAL, "hostname", NULL))) {
         EC_NULL_LOG( options->hostname = strdup(p) );
     } else {
@@ -1550,11 +1840,11 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
             LOG(log_debug, logtype_afpd, "Locale charset is '%s'", p);
 #else /* system doesn't have LOCALE support */
             LOG(log_warning, logtype_afpd, "system doesn't have LOCALE support");
-            p = strdup("UTF8");
+            p = "UTF8";
 #endif
         }
         if (strcasecmp(p, "UTF-8") == 0) {
-            p = strdup("UTF8");
+            p = "UTF8";
         }
         options->unixcodepage = strdup(p);
         set_charset_name(CH_UNIX, p);
@@ -1567,7 +1857,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->volcodepage = strdup(options->unixcodepage);
     } else {
         if (strcasecmp(p, "UTF-8") == 0) {
-            p = strdup("UTF8");
+            p = "UTF8";
         }
         options->volcodepage = strdup(p);
     }
@@ -1587,6 +1877,11 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->maccharset = CH_MAC;
     LOG(log_debug, logtype_afpd, "Global mac charset is %s", options->maccodepage);
 
+    if (readextmap(options->extmapfile) != 0) {
+        LOG(log_error, logtype_afpd, "Couldn't load extension -> type/creator mappings file \"%s\"",
+            options->extmapfile);
+    }
+
     /* Check for sane values */
     if (options->tickleval <= 0)
         options->tickleval = 30;
@@ -1606,3 +1901,82 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
 EC_CLEANUP:
     EC_EXIT;
 }
+
+#define CONFIG_ARG_FREE(a) do {                     \
+    free(a);                                        \
+    a = NULL;                                       \
+    } while (0);
+
+/* get rid of any allocated afp_option buffers. */
+void afp_config_free(AFPObj *obj)
+{
+    if (obj->options.configfile)
+        CONFIG_ARG_FREE(obj->options.configfile);
+    if (obj->options.sigconffile)
+        CONFIG_ARG_FREE(obj->options.sigconffile);
+    if (obj->options.uuidconf)
+        CONFIG_ARG_FREE(obj->options.uuidconf);
+    if (obj->options.logconfig)
+        CONFIG_ARG_FREE(obj->options.logconfig);
+    if (obj->options.logfile)
+        CONFIG_ARG_FREE(obj->options.logfile);
+    if (obj->options.loginmesg)
+        CONFIG_ARG_FREE(obj->options.loginmesg);
+    if (obj->options.guest)
+        CONFIG_ARG_FREE(obj->options.guest);
+    if (obj->options.extmapfile)
+        CONFIG_ARG_FREE(obj->options.extmapfile);
+    if (obj->options.passwdfile)
+        CONFIG_ARG_FREE(obj->options.passwdfile);
+    if (obj->options.uampath)
+        CONFIG_ARG_FREE(obj->options.uampath);
+    if (obj->options.uamlist)
+        CONFIG_ARG_FREE(obj->options.uamlist);
+    if (obj->options.port)
+        CONFIG_ARG_FREE(obj->options.port);
+    if (obj->options.signatureopt)
+        CONFIG_ARG_FREE(obj->options.signatureopt);
+    if (obj->options.k5service)
+        CONFIG_ARG_FREE(obj->options.k5service);
+    if (obj->options.k5realm)
+        CONFIG_ARG_FREE(obj->options.k5realm);
+    if (obj->options.listen)
+        CONFIG_ARG_FREE(obj->options.listen);
+    if (obj->options.interfaces)
+        CONFIG_ARG_FREE(obj->options.interfaces);
+    if (obj->options.ntdomain)
+        CONFIG_ARG_FREE(obj->options.ntdomain);
+    if (obj->options.addomain)
+        CONFIG_ARG_FREE(obj->options.addomain);
+    if (obj->options.ntseparator)
+        CONFIG_ARG_FREE(obj->options.ntseparator);
+    if (obj->options.mimicmodel)
+        CONFIG_ARG_FREE(obj->options.mimicmodel);
+    if (obj->options.adminauthuser)
+        CONFIG_ARG_FREE(obj->options.adminauthuser);
+    if (obj->options.hostname)
+        CONFIG_ARG_FREE(obj->options.hostname);
+    if (obj->options.k5keytab)
+        CONFIG_ARG_FREE(obj->options.k5keytab);
+    if (obj->options.Cnid_srv)
+        CONFIG_ARG_FREE(obj->options.Cnid_srv);
+    if (obj->options.Cnid_port)
+        CONFIG_ARG_FREE(obj->options.Cnid_port);
+    if (obj->options.fqdn)
+        CONFIG_ARG_FREE(obj->options.fqdn);
+
+    if (obj->options.unixcodepage)
+        CONFIG_ARG_FREE(obj->options.unixcodepage);
+    if (obj->options.maccodepage)
+        CONFIG_ARG_FREE(obj->options.maccodepage);
+    if (obj->options.volcodepage)
+        CONFIG_ARG_FREE(obj->options.volcodepage);
+
+    obj->options.flags = 0;
+    obj->options.passwdbits = 0;
+
+    /* Free everything called from afp_config_parse() */
+    free_extmap();
+    iniparser_freedict(obj->iniconfig);
+    free_charset_names();
+}
index ada3e61052353b972776eae3fa85046dac7057f1..da78c6a064bd637418b40f5495653b2f77f5ebdf 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 2013 Frank Lahm <franklahm@gmail.com
  * All rights reserved. See COPYRIGHT.
  *
- *
  * handle inserting, removing, and freeing of children.
  * this does it via a hash table. it incurs some overhead over
  * a linear append/remove in total removal and kills, but it makes
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/time.h>
+#include <pthread.h>
 
 #include <atalk/logger.h>
 #include <atalk/errchk.h>
 #endif
 
 /* hash/child functions: hash OR's pid */
-#define CHILD_HASHSIZE 32
 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
 
-typedef struct server_child_fork {
-    struct server_child_data *table[CHILD_HASHSIZE];
-    void (*cleanup)(const pid_t);
-} server_child_fork;
-
-static inline void hash_child(struct server_child_data **htable,
-                              struct server_child_data *child)
+static inline void hash_child(afp_child_t **htable, afp_child_t *child)
 {
-    struct server_child_data **table;
+    afp_child_t **table;
 
-    table = &htable[HASH(child->pid)];
-    if ((child->next = *table) != NULL)
-        (*table)->prevp = &child->next;
+    table = &htable[HASH(child->afpch_pid)];
+    if ((child->afpch_next = *table) != NULL)
+        (*table)->afpch_prevp = &child->afpch_next;
     *table = child;
-    child->prevp = table;
+    child->afpch_prevp = table;
 }
 
-static inline void unhash_child(struct server_child_data *child)
+static inline void unhash_child(afp_child_t *child)
 {
-    if (child->prevp) {
-        if (child->next)
-            child->next->prevp = child->prevp;
-        *(child->prevp) = child->next;
+    if (child->afpch_prevp) {
+        if (child->afpch_next)
+            child->afpch_next->afpch_prevp = child->afpch_prevp;
+        *(child->afpch_prevp) = child->afpch_next;
     }
 }
 
-static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
+afp_child_t *server_child_resolve(server_child_t *childs, id_t pid)
 {
-    struct server_child_data *child;
+    afp_child_t *child;
 
-    for (child = table[HASH(pid)]; child; child = child->next) {
-        if (child->pid == pid)
+    for (child = childs->servch_table[HASH(pid)]; child; child = child->afpch_next) {
+        if (child->afpch_pid == pid)
             break;
     }
 
@@ -89,23 +83,15 @@ static struct server_child_data *resolve_child(struct server_child_data **table,
 }
 
 /* initialize server_child structure */
-server_child *server_child_alloc(const int connections, const int nforks)
+server_child_t *server_child_alloc(int connections)
 {
-    server_child *children;
-
-    children = (server_child *) calloc(1, sizeof(server_child));
-    if (!children)
-        return NULL;
-
-    children->nsessions = connections;
-    children->nforks = nforks;
-    children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+    server_child_t *children;
 
-    if (!children->fork) {
-        free(children);
+    if (!(children = (server_child_t *)calloc(1, sizeof(server_child_t))))
         return NULL;
-    }
 
+    children->servch_nsessions = connections;
+    pthread_mutex_init(&children->servch_lock, NULL);
     return children;
 }
 
@@ -113,17 +99,11 @@ server_child *server_child_alloc(const int connections, const int nforks)
  * add a child
  * @return pointer to struct server_child_data on success, NULL on error
  */
-afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, int ipc_fd)
+afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
 {
-    server_child_fork *fork;
     afp_child_t *child = NULL;
-    sigset_t sig, oldsig;
 
-    /* we need to prevent deletions from occuring before we get a
-     * chance to add the child in. */
-    sigemptyset(&sig);
-    sigaddset(&sig, SIGCHLD);
-    pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
+    pthread_mutex_lock(&children->servch_lock);
 
     /* it's possible that the child could have already died before the
      * pthread_sigmask. we need to check for this. */
@@ -132,118 +112,106 @@ afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, int
         goto exit;
     }
 
-    fork = (server_child_fork *) children->fork + forkid;
-
     /* if we already have an entry. just return. */
-    if (child = resolve_child(fork->table, pid))
+    if ((child = server_child_resolve(children, pid)))
         goto exit;
 
     if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
         goto exit;
 
-    child->pid = pid;
-    child->valid = 0;
-    child->killed = 0;
-    child->ipc_fd = ipc_fd;
+    child->afpch_pid = pid;
+    child->afpch_ipc_fd = ipc_fd;
+    child->afpch_logintime = time(NULL);
 
-    hash_child(fork->table, child);
-    children->count++;
+    hash_child(children->servch_table, child);
+    children->servch_count++;
 
 exit:
-    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    pthread_mutex_unlock(&children->servch_lock);
     return child;
 }
 
 /* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid, pid_t pid)
+int server_child_remove(server_child_t *children, pid_t pid)
 {
     int fd;
-    server_child_fork *fork;
-    struct server_child_data *child;
+    afp_child_t *child;
 
-    fork = (server_child_fork *) children->fork + forkid;
-    if (!(child = resolve_child(fork->table, pid)))
+    if (!(child = server_child_resolve(children, pid)))
         return -1;
 
+    pthread_mutex_lock(&children->servch_lock);
+
     unhash_child(child);
-    if (child->clientid) {
-        free(child->clientid);
-        child->clientid = NULL;
+    if (child->afpch_clientid) {
+        free(child->afpch_clientid);
+        child->afpch_clientid = NULL;
     }
 
     /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
-    fd = child->ipc_fd;
+    fd = child->afpch_ipc_fd;
     if (fd != -1)
         close(fd);
 
     free(child);
-    children->count--;
+    children->servch_count--;
 
-    if (fork->cleanup)
-        fork->cleanup(pid);
+    pthread_mutex_unlock(&children->servch_lock);
 
     return fd;
 }
 
 /* free everything: by using a hash table, this increases the cost of
  * this part over a linked list by the size of the hash table */
-void server_child_free(server_child *children)
+void server_child_free(server_child_t *children)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
-    int i, j;
-
-    for (i = 0; i < children->nforks; i++) {
-        fork = (server_child_fork *) children->fork + i;
-        for (j = 0; j < CHILD_HASHSIZE; j++) {
-            child = fork->table[j]; /* start at the beginning */
-            while (child) {
-                tmp = child->next;
-                close(child->ipc_fd);
-                if (child->clientid) {
-                    free(child->clientid);
-                }
-                free(child);
-                child = tmp;
-            }
+    afp_child_t *child, *tmp;
+    int j;
+
+    for (j = 0; j < CHILD_HASHSIZE; j++) {
+        child = children->servch_table[j]; /* start at the beginning */
+        while (child) {
+            tmp = child->afpch_next;
+            close(child->afpch_ipc_fd);
+            if (child->afpch_clientid)
+                free(child->afpch_clientid);
+            if (child->afpch_volumes)
+                free(child->afpch_volumes);
+            free(child);
+            child = tmp;
         }
     }
-    free(children->fork);
+
     free(children);
 }
 
 /* send signal to all child processes */
-void server_child_kill(server_child *children, int forkid, int sig)
+void server_child_kill(server_child_t *children, int sig)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
+    afp_child_t *child, *tmp;
     int i;
 
-    fork = (server_child_fork *) children->fork + forkid;
     for (i = 0; i < CHILD_HASHSIZE; i++) {
-        child = fork->table[i];
+        child = children->servch_table[i];
         while (child) {
-            tmp = child->next;
-            kill(child->pid, sig);
+            tmp = child->afpch_next;
+            kill(child->afpch_pid, sig);
             child = tmp;
         }
     }
 }
 
-/* send kill to a child processes.
- * a plain-old linked list
- * FIXME use resolve_child ?
- */
-static int kill_child(struct server_child_data *child)
+/* send kill to a child processes */
+static int kill_child(afp_child_t *child)
 {
-    if (!child->killed) {
-        kill(child->pid, SIGTERM);
+    if (!child->afpch_killed) {
+        kill(child->afpch_pid, SIGTERM);
         /* we don't wait because there's no guarantee that we can really kill it */
-        child->killed = 1;
+        child->afpch_killed = 1;
         return 1;
     } else {
-        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
-        kill(child->pid, SIGKILL);
+        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->afpch_pid);
+        kill(child->afpch_pid, SIGKILL);
     }
     return 1;
 }
@@ -252,20 +220,16 @@ static int kill_child(struct server_child_data *child)
  * Try to find an old session and pass socket
  * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
  */
-int server_child_transfer_session(server_child *children,
-                                  int forkid,
+int server_child_transfer_session(server_child_t *children,
                                   pid_t pid,
                                   uid_t uid,
                                   int afp_socket,
                                   uint16_t DSI_requestID)
 {
     EC_INIT;
-    server_child_fork *fork;
-    struct server_child_data *child;
-    int i;
+    afp_child_t *child;
 
-    fork = (server_child_fork *) children->fork + forkid;
-    if ((child = resolve_child(fork->table, pid)) == NULL) {
+    if ((child = server_child_resolve(children, pid)) == NULL) {
         LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
         if (kill(pid, 0) == 0) {
             LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
@@ -280,23 +244,23 @@ int server_child_transfer_session(server_child *children,
         return 0;
     }
 
-    if (!child->valid) {
+    if (!child->afpch_valid) {
         /* hmm, client 'guess' the pid, rogue? */
         LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
         return 0;
-    } else if (child->uid != uid) {
+    } else if (child->afpch_uid != uid) {
         LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
         return 0;
     }
 
     LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
     
-    if (writet(child->ipc_fd, &DSI_requestID, 2, 0, 2) != 2) {
+    if (writet(child->afpch_ipc_fd, &DSI_requestID, 2, 0, 2) != 2) {
         LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
         EC_STATUS(-1);
         goto EC_CLEANUP;
     }
-    EC_ZERO_LOG(send_fd(child->ipc_fd, afp_socket));
+    EC_ZERO_LOG(send_fd(child->afpch_ipc_fd, afp_socket));
     EC_ZERO_LOG(kill(pid, SIGURG));
 
     EC_STATUS(1);
@@ -308,65 +272,55 @@ EC_CLEANUP:
 
 /* see if there is a process for the same mac     */
 /* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
                                  uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
 {
-    server_child_fork *fork;
-    struct server_child_data *child, *tmp;
+    afp_child_t *child, *tmp;
     int i;
 
-    fork = (server_child_fork *)children->fork + forkid;
-
+    pthread_mutex_lock(&children->servch_lock);
+    
     for (i = 0; i < CHILD_HASHSIZE; i++) {
-        child = fork->table[i];
+        child = children->servch_table[i];
         while (child) {
-            tmp = child->next;
-            if ( child->pid != pid) {
-                if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
-                    if ( child->time != boottime ) {
+            tmp = child->afpch_next;
+            if (child->afpch_pid != pid) {
+                if (child->afpch_idlen == idlen && memcmp(child->afpch_clientid, id, idlen) == 0) {
+                    if ( child->afpch_boottime != boottime ) {
                         /* Client rebooted */
-                        if (uid == child->uid) {
+                        if (uid == child->afpch_uid) {
                             kill_child(child);
                             LOG(log_warning, logtype_default,
                                 "Terminated disconnected child[%u], client rebooted.",
-                                child->pid);
+                                child->afpch_pid);
                         } else {
                             LOG(log_warning, logtype_default,
-                                "Session with different pid[%u]", child->pid);
+                                "Session with different pid[%u]", child->afpch_pid);
                         }
                     } else {
                         /* One client with multiple sessions */
                         LOG(log_debug, logtype_default,
-                            "Found another session[%u] for client[%u]", child->pid, pid);
+                            "Found another session[%u] for client[%u]", child->afpch_pid, pid);
                     }
                 }
             } else {
                 /* update childs own slot */
-                child->time = boottime;
-                if (child->clientid)
-                    free(child->clientid);
-                LOG(log_debug, logtype_default, "Setting client ID for %u", child->pid);
-                child->uid = uid;
-                child->valid = 1;
-                child->idlen = idlen;
-                child->clientid = id;
+                child->afpch_boottime = boottime;
+                if (child->afpch_clientid)
+                    free(child->afpch_clientid);
+                LOG(log_debug, logtype_default, "Setting client ID for %u", child->afpch_pid);
+                child->afpch_uid = uid;
+                child->afpch_valid = 1;
+                child->afpch_idlen = idlen;
+                child->afpch_clientid = id;
             }
             child = tmp;
         }
     }
-}
 
-/* for extra cleanup if necessary */
-void server_child_setup(server_child *children, const int forkid,
-                        void (*fcn)(const pid_t))
-{
-    server_child_fork *fork;
-
-    fork = (server_child_fork *) children->fork + forkid;
-    fork->cleanup = fcn;
+    pthread_mutex_unlock(&children->servch_lock);
 }
 
-
 /* ---------------------------
  * reset children signals
  */
index d40e3a49ea4cb07bf1b7984fc418cb5ec11521ee..e49d165d462133ba5dc3c1b222ff5d4d99621116 100644 (file)
@@ -18,6 +18,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <time.h>
+#include <pthread.h>
 
 #include <atalk/server_child.h>
 #include <atalk/server_ipc.h>
@@ -42,13 +43,15 @@ typedef struct ipc_header {
 } ipc_header_t;
 
 static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
-                               "IPC_GETSESSION"};
+                               "IPC_GETSESSION",
+                               "IPC_STATE",
+                               "IPC_VOLUMES"};
 
 /*
  * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
  * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
  */
-static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
+static int ipc_kill_token(struct ipc_header *ipc, server_child_t *children)
 {
     pid_t pid;
 
@@ -59,7 +62,6 @@ static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
     memcpy (&pid, ipc->msg, sizeof(pid_t));
 
     return server_child_transfer_session(children,
-                                         CHILD_DSIFORK,
                                          pid,
                                          ipc->uid,
                                          ipc->afp_socket,
@@ -67,7 +69,7 @@ static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
 }
 
 /* ----------------- */
-static int ipc_get_session(struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child_t *children)
 {
     uint32_t boottime;
     uint32_t idlen;
@@ -96,7 +98,6 @@ static int ipc_get_session(struct ipc_header *ipc, server_child *children)
         ipc->child_pid, ipc->uid, boottime); 
 
     server_child_kill_one_by_id(children,
-                                CHILD_DSIFORK,
                                 ipc->child_pid,
                                 ipc->uid,
                                 idlen,
@@ -106,100 +107,48 @@ static int ipc_get_session(struct ipc_header *ipc, server_child *children)
     return 0;
 }
 
-/***********************************************************************************
- * Public functions
- ***********************************************************************************/
-
-/*!
- * Listen on UNIX domain socket "name" for IPC from old sesssion
- *
- * @args name    (r) file name to use for UNIX domain socket
- * @returns      socket fd, -1 on error
- */
-int ipc_server_uds(const char *name)
+static int ipc_set_state(struct ipc_header *ipc, server_child_t *children)
 {
     EC_INIT;
-    struct sockaddr_un address;
-    socklen_t address_length;
-    int fd = -1;
-
-    EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
-    EC_ZERO_LOG( setnonblock(fd, 1) );
-    unlink(name);
-    address.sun_family = AF_UNIX;
-    address_length = sizeof(address.sun_family) + sprintf(address.sun_path, "%s", name);
-    EC_ZERO_LOG( bind(fd, (struct sockaddr *)&address, address_length) );
-    EC_ZERO_LOG( listen(fd, 1024) );
+    afp_child_t *child;
 
-EC_CLEANUP:
-    if (ret != 0) {
-        return -1;
-    }
+    pthread_mutex_lock(&children->servch_lock);
+
+    if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
+        EC_FAIL;
+
+    memcpy(&child->afpch_state, ipc->msg, sizeof(uint16_t));
 
-    return fd;
+EC_CLEANUP:
+    pthread_mutex_unlock(&children->servch_lock);
+    EC_EXIT;
 }
 
-/*!
- * Connect to UNIX domain socket "name" for IPC with new afpd master
- *
- * 1. Connect
- * 2. send pid, which establishes a child structure for us in the master
- *
- * @args name    (r) file name to use for UNIX domain socket
- * @returns      socket fd, -1 on error
- */
-int ipc_client_uds(const char *name)
+static int ipc_set_volumes(struct ipc_header *ipc, server_child_t *children)
 {
     EC_INIT;
-    struct sockaddr_un address;
-    socklen_t address_length;
-    int fd = -1;
-    pid_t pid = getpid();
-
-    EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
-    address.sun_family = AF_UNIX;
-    address_length = sizeof(address.sun_family) + sprintf(address.sun_path, "%s", name);
-
-    EC_ZERO_LOG( connect(fd, (struct sockaddr *)&address, address_length) ); /* 1 */
-    LOG(log_debug, logtype_afpd, "ipc_client_uds: connected to master");
+    afp_child_t *child;
 
-    EC_ZERO_LOG( setnonblock(fd, 1) );
+    pthread_mutex_lock(&children->servch_lock);
 
-    if (writet(fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
-        LOG(log_error, logtype_afpd, "ipc_client_uds: writet: %s", strerror(errno));
+    if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
         EC_FAIL;
+
+    if (child->afpch_volumes) {
+        free(child->afpch_volumes);
+        child->afpch_volumes = NULL;
     }
+    if (ipc->len)
+        child->afpch_volumes = strdup(ipc->msg);
 
 EC_CLEANUP:
-    if (ret != 0) {
-        return -1;
-    }
-    LOG(log_debug, logtype_afpd, "ipc_client_uds: fd: %d", fd);
-    return fd;
+    pthread_mutex_unlock(&children->servch_lock);
+    EC_EXIT;
 }
 
-int reconnect_ipc(AFPObj *obj)
-{
-    int retrycount = 0;
-
-    LOG(log_debug, logtype_afpd, "reconnect_ipc: start");
-
-    close(obj->ipc_fd);
-    obj->ipc_fd = -1;
-
-    sleep((getpid() % 5) + 15);  /* give it enough time to start */
-
-    while (retrycount++ < 10) {
-        if ((obj->ipc_fd = ipc_client_uds(_PATH_AFP_IPC)) == -1) {
-            LOG(log_error, logtype_afpd, "reconnect_ipc: cant reconnect to master");
-            sleep(1);
-            continue;
-        }
-        LOG(log_debug, logtype_afpd, "reconnect_ipc: succesfull IPC reconnect");
-        return 0;
-    }
-    return -1;
-}
+/***********************************************************************************
+ * Public functions
+ ***********************************************************************************/
 
 /* ----------------- 
  * Ipc format
@@ -219,7 +168,7 @@ int reconnect_ipc(AFPObj *obj)
  *
  * @returns -1 on error, 0 on success
  */
-int ipc_server_read(server_child *children, int fd)
+int ipc_server_read(server_child_t *children, int fd)
 {
     int       ret;
     struct ipc_header ipc;
@@ -267,8 +216,6 @@ int ipc_server_read(server_child *children, int fd)
     LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
         ipc_cmd_str[ipc.command], ipc.child_pid); 
 
-    int afp_socket;
-
     switch (ipc.command) {
 
        case IPC_DISCOLDSESSION:
@@ -296,6 +243,16 @@ int ipc_server_read(server_child *children, int fd)
             return -1;
         break;
 
+    case IPC_STATE:
+        if (ipc_set_state(&ipc, children) != 0)
+            return -1;
+        break;
+
+    case IPC_VOLUMES:
+        if (ipc_set_volumes(&ipc, children) != 0)
+            return -1;
+        break;
+
        default:
                LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
                return -1;
@@ -347,3 +304,8 @@ int ipc_child_write(int fd, uint16_t command, int len, void *msg)
 
    return 0;
 }
+
+int ipc_child_state(AFPObj *obj, uint16_t state)
+{
+    return ipc_child_write(obj->ipc_fd, IPC_STATE, sizeof(uint16_t), &state);
+}
index 0251eadaeba94652c2b45474881f648ea1c5dde2..6ae817e5547c701891ee7e2d6dcfd554283b9870 100644 (file)
@@ -126,9 +126,7 @@ int check_lockfile(const char *program, const char *pidfile)
  */
 int create_lockfile(const char *program, const char *pidfile)
 {
-    char buf[10];
     FILE *pf;
-    pid_t pid;
     int mask;
   
     if (check_lockfile(program, pidfile) != 0)
index eda69c6c7e078c583e031add2aad2bd89d95d020..acea8ad39218dfa03fa6ec828abf4030c25433db 100644 (file)
@@ -79,7 +79,7 @@ int setnonblock(int fd, int cmd)
  * @param lenght          (r)  how many bytes to read
  * @param setnonblocking  (r)  when non-zero this func will enable and disable non blocking
  *                             io mode for the socket
- * @param timeout         (r)  number of seconds to try reading
+ * @param timeout         (r)  number of seconds to try reading, 0 means no timeout
  *
  * @returns number of bytes actually read or -1 on timeout or error
  */
@@ -99,44 +99,50 @@ ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, i
     }
 
     /* Calculate end time */
-    (void)gettimeofday(&now, NULL);
-    end = now;
-    end.tv_sec += timeout;
+    if (timeout) {
+        (void)gettimeofday(&now, NULL);
+        end = now;
+        end.tv_sec += timeout;
+    }
 
     while (stored < length) {
-        len = read(socket, (char *) data + stored, length - stored);
+        len = recv(socket, (char *) data + stored, length - stored, 0);
         if (len == -1) {
             switch (errno) {
             case EINTR:
                 continue;
             case EAGAIN:
                 FD_SET(socket, &rfds);
-                tv.tv_usec = 0;
-                tv.tv_sec  = timeout;
-                        
-                while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+                if (timeout) {
+                    tv.tv_usec = 0;
+                    tv.tv_sec  = timeout;
+                }
+
+                while ((ret = select(socket + 1, &rfds, NULL, NULL, timeout ? &tv : NULL)) < 1) {
                     switch (ret) {
                     case 0:
-                        LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+                        LOG(log_debug, logtype_dsi, "select timeout %d s", timeout);
                         errno = EAGAIN;
                         goto exit;
 
                     default: /* -1 */
                         switch (errno) {
                         case EINTR:
-                            (void)gettimeofday(&now, NULL);
-                            if (now.tv_sec > end.tv_sec
-                                ||
-                                (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
-                                LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
-                                goto exit;
-                            }
-                            if (now.tv_usec > end.tv_usec) {
-                                tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
-                                tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
-                            } else {
-                                tv.tv_usec = end.tv_usec - now.tv_usec;
-                                tv.tv_sec  = end.tv_sec - now.tv_sec;
+                            if (timeout) {
+                                (void)gettimeofday(&now, NULL);
+                                if (now.tv_sec > end.tv_sec
+                                    ||
+                                    (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
+                                    LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+                                    goto exit;
+                                }
+                                if (now.tv_usec > end.tv_usec) {
+                                    tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+                                    tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
+                                } else {
+                                    tv.tv_usec = end.tv_usec - now.tv_usec;
+                                    tv.tv_sec  = end.tv_sec - now.tv_sec;
+                                }
                             }
                             FD_SET(socket, &rfds);
                             continue;
index 56c3e168eaea05c05705464a0f21830aedf60c03..37bfe140fe3d1d0101ab966a55ee90db3f67b9d8 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/time.h>
 #include <time.h>
 #include <sys/wait.h>
+#include <libgen.h>
 
 #include <atalk/adouble.h>
 #include <atalk/ea.h>
@@ -227,20 +228,103 @@ char *stripped_slashes_basename(char *p)
     return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
 }
 
+/*********************************************************************************
+ * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
+ * Currently the only used options are O_NOFOLLOW, used to switch between symlink
+ * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
+ * called which does special ACL handling depending on the filesytem
+ *********************************************************************************/
+
+int ostat(const char *path, struct stat *buf, int options)
+{
+    if (options & O_NOFOLLOW)
+        return lstat(path, buf);
+    else
+        return stat(path, buf);
+}
+
+int ochown(const char *path, uid_t owner, gid_t group, int options)
+{
+    if (options & O_NOFOLLOW)
+        return lchown(path, owner, group);
+    else
+        return chown(path, owner, group);
+}
+
+/*!
+ * chmod() wrapper for symlink and ACL handling
+ *
+ * @param path       (r) path
+ * @param mode       (r) requested mode
+ * @param sb         (r) stat() of path or NULL
+ * @param option     (r) O_NOFOLLOW | O_NETATALK_ACL
+ *
+ * Options description:
+ * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
+ * O_NETATALK_ACL: call chmod_acl() instead of chmod()
+ */
+int ochmod(char *path, mode_t mode, const struct stat *st, int options)
+{
+    struct stat sb;
+
+    if (!st) {
+        if (lstat(path, &sb) != 0)
+            return -1;
+        st = &sb;
+    }
+
+    if (options & O_NOFOLLOW)
+        if (S_ISLNK(st->st_mode))
+            return 0;
+
+    if (options & O_NETATALK_ACL) {
+        return chmod_acl(path, mode);
+    } else {
+        return chmod(path, mode);
+    }
+}
+
+/* 
+ * @brief ostat/fsstatat multiplexer
+ *
+ * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
+ *
+ * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
+ * @param path    (r) pathname
+ * @param st      (rw) pointer to struct stat
+ */
+int ostatat(int dirfd, const char *path, struct stat *st, int options)
+{
+#ifdef HAVE_ATFUNCS
+    if (dirfd == -1)
+        dirfd = AT_FDCWD;
+    return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#else
+    return ostat(path, st, options);
+#endif            
+
+    /* DEADC0DE */
+    return -1;
+}
+
 /*!
  * @brief symlink safe chdir replacement
  *
- * Only chdirs to dir if it doesn't contain symlinks.
+ * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
+ * is disabled
  *
  * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
  */
-int lchdir(const char *dir)
+int ochdir(const char *dir, int options)
 {
     char buf[MAXPATHLEN+1];
     char cwd[MAXPATHLEN+1];
     char *test;
     int  i;
 
+    if (!(options & O_NOFOLLOW))
+        return chdir(dir);
+
     /*
      dir is a canonical path (without "../" "./" "//" )
      but may end with a / 
@@ -342,3 +426,126 @@ int gmem(gid_t gid, int ngroups, gid_t *groups)
     }
     return( 0 );
 }
+
+/*
+ * realpath() replacement that always allocates storage for returned path
+ */
+char *realpath_safe(const char *path)
+{
+    char *resolved_path;
+
+#ifdef REALPATH_TAKES_NULL
+    if ((resolved_path = realpath(path, NULL)) == NULL) {
+        LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+        return NULL;
+    }
+    return resolved_path;
+#else
+    if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
+        return NULL;
+    if (realpath(path, resolved_path) == NULL) {
+        free(resolved_path);
+        LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+        return NULL;
+    }
+    /* Safe some memory */
+    char *tmp;
+    if ((tmp = strdup(resolved_path)) == NULL) {
+        free(resolved_path);
+        return NULL;
+    }
+    free(resolved_path);
+    resolved_path = tmp;
+    return resolved_path;
+#endif
+}
+
+/**
+ * Returns pointer to static buffer with basename of path
+ **/
+const char *basename_safe(const char *path)
+{
+    static char buf[MAXPATHLEN+1];
+    strlcpy(buf, path, MAXPATHLEN);
+    return basename(buf);
+}
+
+/**
+ * extended strtok allows the quoted strings
+ * modified strtok.c in glibc 2.0.6
+ **/
+char *strtok_quote(char *s, const char *delim)
+{
+    static char *olds = NULL;
+    char *token;
+
+    if (s == NULL)
+        s = olds;
+
+    /* Scan leading delimiters.  */
+    s += strspn (s, delim);
+    if (*s == '\0')
+        return NULL;
+
+    /* Find the end of the token.  */
+    token = s;
+
+    if (token[0] == '\"') {
+        token++;
+        s = strpbrk (token, "\"");
+    } else {
+        s = strpbrk (token, delim);
+    }
+
+    if (s == NULL) {
+        /* This token finishes the string.  */
+        olds = strchr (token, '\0');
+    } else {
+        /* Terminate the token and make OLDS point past it.  */
+        *s = '\0';
+        olds = s + 1;
+    }
+    return token;
+}
+
+int set_groups(AFPObj *obj, struct passwd *pwd)
+{
+    if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
+        LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
+
+    if ((obj->ngroups = getgroups(0, NULL)) < 0) {
+        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
+        return -1;
+    }
+
+    if (obj->groups)
+        free(obj->groups);
+    if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
+        LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
+        return -1;
+    }
+
+    if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) {
+        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+#define GROUPSTR_BUFSIZE 1024
+const char *print_groups(int ngroups, gid_t *groups)
+{
+    static char groupsstr[GROUPSTR_BUFSIZE];
+    int i;
+    char *s = groupsstr;
+
+    if (ngroups == 0)
+        return "-";
+
+    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
+        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
+    }
+
+    return groupsstr;
+}
index c936f178d79bbd8bbbafc8c0269a07275ac550a7..d7c8eb62b46470d2caed350b0b095d48d5ea1b05 100644 (file)
@@ -108,7 +108,7 @@ int remove_acl_vfs(const char *name)
 
 
     /* Remove default ACL if it's a dir */
-    EC_ZERO_LOG_ERR(stat(name, &st), AFPERR_MISC);
+    EC_ZERO_ERR(stat(name, &st), AFPERR_MISC);
     if (S_ISDIR(st.st_mode)) {
         EC_NULL_LOG_ERR(acl = acl_init(0), AFPERR_MISC);
         EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, acl), AFPERR_MISC);
@@ -129,6 +129,7 @@ int remove_acl_vfs(const char *name)
     EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
 
 EC_CLEANUP:
+    if (errno == ENOENT) EC_STATUS(0);
     if (acl) acl_free(acl);
 
     EC_EXIT;
index cdc7c82820b32837e1832be46054491a5a88898c..19c23e0672c8ac78ba2287b448f19bffe0b6baf9 100644 (file)
@@ -79,7 +79,7 @@ static char *mtoupath(const struct vol *vol, const char *mpath)
     char         *u;
     size_t       inplen;
     size_t       outlen;
-    uint16_t     flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
+    uint16_t     flags = CONV_ESCAPEHEX;
 
     if (!mpath)
         return NULL;
@@ -906,7 +906,7 @@ int ea_close(struct ea * restrict ea)
             if (ea->ea_count == 0) {
                 /* Check if EA header exists and remove it */
                 eaname = ea_path(ea, NULL, 0);
-                if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
+                if ((statat(ea->dirfd, eaname, &st)) == 0) {
                     if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
                             eaname, strerror(errno));
@@ -1583,7 +1583,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
         }
     }
 
-    if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
+    if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) {
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1600,7 +1600,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((lchown(eaname, uid, gid)) != 0) {
+        if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) {
             switch (errno) {
             case EPERM:
             case EACCES:
@@ -1644,7 +1644,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
     }
 
     /* Set mode on EA header file */
-    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+    if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
@@ -1663,7 +1663,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+        if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
             switch (errno) {
             case EPERM:
@@ -1712,7 +1712,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
     }
 
     /* Set mode on EA header */
-    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+    if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
@@ -1742,7 +1742,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+        if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
             switch (errno) {
             case EPERM:
index a30e6d96b5b40121d3d8cebf434cb53fc8942a47..374248bc8a9e4696eedf28519aeeec5a80fa7761 100644 (file)
@@ -439,6 +439,9 @@ int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
                if (!*name)
                        continue;
 
+        if (STRCMP(name, ==, AD_EA_META))
+            continue;
+
         if (sfd != -1) {
             if (fchdir(sfd) == -1) {
                 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s",
index 26da70636ff1a683c19d124a631105798f0d0541..73724d53080d66908ae3c07fa9166f58c10bd45e 100644 (file)
@@ -876,7 +876,6 @@ static int solaris_attropen(const char *path, const char *attrpath, int oflag, m
             EC_FAIL;
         default:
             LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
-            errno = ENOATTR;
             EC_FAIL;
         }
        }
@@ -888,7 +887,6 @@ static int solaris_attropen(const char *path, const char *attrpath, int oflag, m
             EC_FAIL;
         default:
             LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
-            errno = ENOATTR;
             EC_FAIL;
         }
        }
@@ -912,6 +910,7 @@ static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
         switch (errno) {
         case ENOENT:
         case EEXIST:
+        case EACCES:
             break;
         default:
             LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
index 1c0556d95e2eebc2e9baeccc34227a112ceeed8a..bc90c75ad539bcdaa7f8a00e14271adf37538092 100644 (file)
@@ -33,7 +33,7 @@ int dir_rx_set(mode_t mode)
 }
 
 /* --------------------- */
-int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
 {
     struct stat sb;
     mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
@@ -44,12 +44,9 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
         st = &sb;
     }
 
-    if (S_ISLNK(st->st_mode))
-        return 0; /* we don't want to change link permissions */
-    
     mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
 
-    if ( chmod_acl( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
+    if (ochmod((char *)name, mode & ~vol->v_umask, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0 && errno != EPERM ) {
         return -1;
     }
     return 0;
@@ -169,9 +166,6 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
     int    ret = 0;
     int    sfd = -1;
     int    dfd = -1;
-    ssize_t cc;
-    size_t  buflen;
-    char   filebuf[NETATALK_DIOSZ_STACK];
 
 #ifdef HAVE_ATFUNCS
     if (dirfd == -1)
@@ -334,29 +328,6 @@ int statat(int dirfd, const char *path, struct stat *st)
     return -1;
 }
 
-/* 
- * @brief lstat/fsstatat multiplexer
- *
- * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
- *
- * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
- * @param path    (r) pathname
- * @param st      (rw) pointer to struct stat
- */
-int lstatat(int dirfd, const char *path, struct stat *st)
-{
-#ifdef HAVE_ATFUNCS
-    if (dirfd == -1)
-        dirfd = AT_FDCWD;
-    return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
-#else
-    return (lstat(path, st));
-#endif            
-
-    /* DEADC0DE */
-    return -1;
-}
-
 /* 
  * @brief opendir wrapper for *at semantics support
  *
index 8495d8c22fba19dd88789b82c6822bfb4a4fb3d6..d79ac05e1142cb14d53bf226ba45e53722ef74ab 100644 (file)
@@ -49,11 +49,11 @@ struct perm {
     gid_t gid;
 };
 
-typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t );
+typedef int (*rf_loop)(const struct vol *, struct dirent *, char *, void *, int);
 
 /* ----------------------------- */
 static int 
-for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask)
+for_each_adouble(const char *from, const char *name, rf_loop fn, const struct vol *vol, void *data, int flag)
 {
     char            buf[ MAXPATHLEN + 1];
     char            *m;
@@ -79,7 +79,7 @@ for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int
         }
         
         strlcat(buf, de->d_name, sizeof(buf));
-        if (fn && (ret = fn(de, buf, data, flag, v_umask))) {
+        if (fn && (ret = fn(vol, de, buf, data, flag))) {
            closedir(dp);
            return ret;
         }
@@ -127,7 +127,7 @@ static int RF_renamedir_adouble(VFS_FUNC_ARGS_RENAMEDIR)
 }
 
 /* ----------------- */
-static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask)
+static int deletecurdir_adouble_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     struct stat st;
     int         err;
@@ -150,34 +150,33 @@ static int RF_deletecurdir_adouble(VFS_FUNC_ARGS_DELETECURDIR)
 
     /* delete stray .AppleDouble files. this happens to get .Parent files
        as well. */
-    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask))) 
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, vol, NULL, 1))) 
         return err;
     return netatalk_rmdir(-1, ".AppleDouble" );
 }
 
 /* ----------------- */
-static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+static int adouble_setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
 {
-    return setfilmode(name, ad_hf_mode(mode), st, v_umask);
+    return setfilmode(vol, name, ad_hf_mode(mode), st);
 }
 
 static int RF_setfilmode_adouble(VFS_FUNC_ARGS_SETFILEMODE)
 {
-    return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask);
+    return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st);
 }
 
 /* ----------------- */
 static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE)
 {
     const char *adouble = vol->ad_path(name, ADFLAGS_DIR );
-    int  dropbox = vol->v_flags;
 
     if (dir_rx_set(mode)) {
         if (chmod_acl(ad_dir(adouble), (DIRBITS | mode) & ~vol->v_umask) < 0 ) 
             return -1;
     }
 
-    if (adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0) 
+    if (adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_DIR ), mode, st) < 0) 
         return -1;
 
     if (!dir_rx_set(mode)) {
@@ -188,18 +187,18 @@ static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE)
 }
 
 /* ----------------- */
-static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+static int setdirmode_adouble_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag)
 {
     mode_t hf_mode = *(mode_t *)data;
     struct stat st;
 
-    if ( stat( name, &st ) < 0 ) {
+    if (ostat(name, &st, vol_syml_opt(vol)) < 0 ) {
         if (flag)
             return 0;
         LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
     }
     else if (!S_ISDIR(st.st_mode)) {
-        if (setfilmode(name, hf_mode , &st, v_umask) < 0) {
+        if (setfilmode(vol, name, hf_mode, &st) < 0) {
                /* FIXME what do we do then? */
         }
     }
@@ -208,7 +207,6 @@ static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data
 
 static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE)
 {
-    int   dropbox = vol->v_flags;
     mode_t hf_mode = ad_hf_mode(mode);
     const char  *adouble = vol->ad_path(name, ADFLAGS_DIR );
     const char  *adouble_p = ad_dir(adouble);
@@ -218,7 +216,7 @@ static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE)
             return -1;
     }
 
-    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, 0, vol->v_umask))
+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, vol, &hf_mode, 0))
         return -1;
 
     if (!dir_rx_set(mode)) {
@@ -257,7 +255,7 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE)
         if (errno == ENOENT) {
                struct adouble    ad;
 
-            if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+            if (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
                 return 0;
 
             /* We are here  because :
@@ -357,24 +355,17 @@ static int RF_solaris_acl(VFS_FUNC_ARGS_ACL)
     struct stat st;
     int len;
 
-    if ((stat(path, &st)) != 0)
-       return -1;
-    if (S_ISDIR(st.st_mode)) {
-       len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-       if (len < 0 || len >=  MAXPATHLEN)
-           return -1;
-       /* set acl on .AppleDouble dir first */
-       if ((acl(buf, cmd, count, aces)) != 0)
-           return -1;
-       /* now set ACL on ressource fork */
-       if ((acl(vol->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0)
-           return -1;
-    } else
-       /* set ACL on ressource fork */
-       if ((acl(vol->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
-           return -1;
-
-    return 0;
+    if ((stat(path, &st)) != 0) {
+        if (errno == ENOENT)
+            return AFP_OK;
+        return AFPERR_MISC;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        /* set ACL on ressource fork */
+        if ((acl(vol->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
+            return AFPERR_MISC;
+    }
+    return AFP_OK;
 }
 
 static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
@@ -383,20 +374,15 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
     static char buf[ MAXPATHLEN + 1];
     int len;
 
-    if (dir) {
-       len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-       if (len < 0 || len >=  MAXPATHLEN)
-           return AFPERR_MISC;
-       /* remove ACL from .AppleDouble/.Parent first */
-       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
-           return ret;
-       /* now remove from .AppleDouble dir */
-       if ((ret = remove_acl_vfs(buf)) != AFP_OK)
-           return ret;
-    } else
-       /* remove ACL from ressource fork */
-       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
-           return ret;
+    if (dir)
+        return AFP_OK;
+
+    /* remove ACL from ressource fork */
+    if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK) {
+        if (errno == ENOENT)
+            return AFP_OK;
+        return ret;
+    }
 
     return AFP_OK;
 }
@@ -406,52 +392,35 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
 static int RF_posix_acl(VFS_FUNC_ARGS_ACL)
 {
     EC_INIT;
-    static char buf[ MAXPATHLEN + 1];
     struct stat st;
-    int len;
 
-    if (S_ISDIR(st.st_mode)) {
-        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-        if (len < 0 || len >=  MAXPATHLEN)
-            EC_FAIL;
-        /* set acl on .AppleDouble dir first */
-        EC_ZERO_LOG(acl_set_file(buf, type, acl));
+    if (stat(path, &st) == -1)
+        EC_FAIL;
 
-        if (type == ACL_TYPE_ACCESS)
-            /* set ACL on ressource fork (".Parent") too */
-            EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_DIR), type, acl));
-    } else {
+    if (!S_ISDIR(st.st_mode)) {
         /* set ACL on ressource fork */
-        EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl));
+        EC_ZERO_ERR( acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl), AFPERR_MISC );
     }
     
 EC_CLEANUP:
-    if (ret != 0)
-        return AFPERR_MISC;
-    return AFP_OK;
+    if (errno == ENOENT)
+        EC_STATUS(AFP_OK);
+    EC_EXIT;
 }
 
 static int RF_posix_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
 {
     EC_INIT;
-    static char buf[ MAXPATHLEN + 1];
-    int len;
 
-    if (dir) {
-        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
-        if (len < 0 || len >=  MAXPATHLEN)
-            return AFPERR_MISC;
-        /* remove ACL from .AppleDouble/.Parent first */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR)), AFPERR_MISC);
+    if (dir)
+        EC_EXIT_STATUS(AFP_OK);
 
-        /* now remove from .AppleDouble dir */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(buf), AFPERR_MISC);
-    } else {
-        /* remove ACL from ressource fork */
-        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC);
-    }
+    /* remove ACL from ressource fork */
+    EC_ZERO_ERR( remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC );
 
 EC_CLEANUP:
+    if (errno == ENOENT)
+        EC_STATUS(AFP_OK);
     EC_EXIT;
 }
 #endif
@@ -487,7 +456,7 @@ static int RF_renamedir_ea(VFS_FUNC_ARGS_RENAMEDIR)
 }
 
 /* Returns 1 if the entry is NOT an ._ file */
-static int deletecurdir_ea_osx_chkifempty_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ea_osx_chkifempty_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     if (de->d_name[0] != '.' || de->d_name[0] == '_')
         return 1;
@@ -495,7 +464,7 @@ static int deletecurdir_ea_osx_chkifempty_loop(struct dirent *de, char *name, vo
     return 0;
 }
 
-static int deletecurdir_ea_osx_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ea_osx_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     int ret;
     
@@ -515,7 +484,7 @@ static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR)
     /* first check if there's really no other file besides files starting with ._ */
     if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
                                 deletecurdir_ea_osx_chkifempty_loop,
-                                NULL, 0, 0)) != 0) {
+                                vol, NULL, 0)) != 0) {
         if (err == 1)
             return AFPERR_DIRNEMPT;
         return AFPERR_MISC;
@@ -524,7 +493,7 @@ static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR)
     /* Now delete orphaned ._ files */
     if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
                                 deletecurdir_ea_osx_loop,
-                                NULL, 0, 0)) != 0)
+                                vol, NULL, 0)) != 0)
         return err;
 
 #endif
@@ -542,7 +511,7 @@ static int RF_setdirunixmode_ea(VFS_FUNC_ARGS_SETDIRUNIXMODE)
 static int RF_setfilmode_ea(VFS_FUNC_ARGS_SETFILEMODE)
 {
 #ifndef HAVE_EAFD
-    return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask);
+    return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st);
 #endif
     return 0;
 }
@@ -643,7 +612,7 @@ static int RF_renamefile_ea(VFS_FUNC_ARGS_RENAMEFILE)
         struct stat st;
 
         err = errno;
-        if (errno == ENOENT && lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+        if (errno == ENOENT && ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
             return 0;
         errno = err;
         return -1;
index 09505bd76688e62c073a84b4357b01d0af730fbe..8114ad3645182830e93861e8517c4539b07a7f89 100644 (file)
@@ -86,9 +86,6 @@ if INSTALL_LIBEVENT
 dist_bin_SCRIPTS = event_rpcgen.py
 endif
 
-pkgconfigdir=$(libdir)/pkgconfig
-LIBEVENT_PKGCONFIG=libevent.pc
-
 # These sources are conditionally added by configure.in or conditionally
 # included from other files.
 PLATFORM_DEPENDENT_SRC = \
@@ -108,16 +105,13 @@ EXTRA_DIST = \
 LIBEVENT_LIBS_LA = libevent.la libevent_core.la libevent_extra.la
 if PTHREADS
 LIBEVENT_LIBS_LA += libevent_pthreads.la
-LIBEVENT_PKGCONFIG += libevent_pthreads.pc
 endif
 if OPENSSL
 LIBEVENT_LIBS_LA += libevent_openssl.la
-LIBEVENT_PKGCONFIG += libevent_openssl.pc
 endif
 
 if INSTALL_LIBEVENT
 lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA)
-pkgconfig_DATA = $(LIBEVENT_PKGCONFIG)
 else
 noinst_LTLIBRARIES =  $(LIBEVENT_LIBS_LA)
 endif
@@ -196,7 +190,7 @@ NO_UNDEFINED =
 MAYBE_CORE =
 endif
 
-GENERIC_LDFLAGS = -version-info $(VERSION_INFO) $(RELEASE) $(NO_UNDEFINED)
+GENERIC_LDFLAGS = -static
 
 libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
 libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
@@ -249,5 +243,7 @@ doxygen: FORCE
        doxygen $(srcdir)/Doxyfile
 FORCE:
 
-DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
+DISTCLEANFILES = *~ ./include/event2/event-config.h
+
+install:
 
index 6c541a8fe0e6fb42c6aecd76629029a3978e594b..8e1f9c69fc3e8ec838d054dcf0598267de4aab04 100644 (file)
@@ -1,5 +1,6 @@
 EXTRA_DIST = \
        afs-check.m4            \
+       ax_pthread.m4           \
        cnid-backend.m4         \
        config-checks.m4        \
        db3-check.m4            \
@@ -17,4 +18,3 @@ EXTRA_DIST = \
        summary.m4              \
        tcp-wrappers.m4         \
        util.m4
-
index 799cfa2649e6fefab6fd3ffe3fdfc0c9a1a19571..48925d88ce9aad993a6762a486d8d2f6bc4ff18b 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: afs-check.m4,v 1.4 2005-08-11 20:15:35 didg Exp $
 dnl Autoconf macro to check whether AFS support should be enabled
 
 AC_DEFUN([AC_NETATALK_AFS_CHECK], [
diff --git a/macros/ax_pthread.m4 b/macros/ax_pthread.m4
new file mode 100644 (file)
index 0000000..9ede48e
--- /dev/null
@@ -0,0 +1,309 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT 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, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 18
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            ax_cv_PTHREAD_PRIO_INHERIT, [
+                AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+        if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+        fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
index d2a5544a29f76ed5d800902fa9a54a9087102113..8eb5864d25710d397d0516b529067333199614bc 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of grep
-dnl $Id: grep-check.m4,v 1.2 2002-02-14 18:02:04 jmarcus Exp $
 
 AC_DEFUN([AC_PROG_GREP], [
 AC_REQUIRE([AC_EXEEXT])dnl
index 34dfc08cc6c31128da4503e6e857a4ab6fc1038d..cb0fc717fe11daf8e074490de3e6292f0c7dc97f 100644 (file)
@@ -1,5 +1,117 @@
 dnl Kitchen sink for configuration macros
 
+dnl Check for docbook
+AC_DEFUN(AX_CHECK_DOCBOOK, [
+  # It's just rude to go over the net to build
+  XSLTPROC_FLAGS=--nonet
+  DOCBOOK_ROOT=
+  XSLTPROC_WORKS=no
+
+  AC_ARG_WITH(docbook,
+    AS_HELP_STRING(
+      [--with-docbook],
+      [Path to Docbook XSL directory]
+    ),
+    [DOCBOOK_ROOT=$withval]
+  )
+
+  if test -n "$DOCBOOK_ROOT" ; then
+    AC_CHECK_PROG(XSLTPROC,xsltproc,xsltproc,)
+    if test -n "$XSLTPROC"; then
+      AC_MSG_CHECKING([whether xsltproc works])
+      DB_FILE="$DOCBOOK_ROOT/html/docbook.xsl"
+      $XSLTPROC $XSLTPROC_FLAGS $DB_FILE >/dev/null 2>&1 << END
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="test">
+</book>
+END
+      if test "$?" = 0; then
+        XSLTPROC_WORKS=yes
+      fi
+      AC_MSG_RESULT($XSLTPROC_WORKS)
+    fi
+  fi
+
+  AC_MSG_CHECKING([whether to build Docbook documentation])
+  AC_MSG_RESULT($XSLTPROC_WORKS)
+
+  AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC_WORKS" = x"yes")
+  AC_SUBST(XSLTPROC_FLAGS)
+  AC_SUBST(DOCBOOK_ROOT)
+  AC_SUBST(XSLTPROC)
+])
+
+dnl Check for dtrace
+AC_DEFUN([AC_NETATALK_DTRACE], [
+  AC_ARG_WITH(dtrace,
+    AS_HELP_STRING(
+      [--with-dtrace],
+      [Enable dtrace probes (default: enabled if dtrace found)]
+    ),
+    [WDTRACE=$withval],
+    [WDTRACE=auto]
+  )
+  if test "x$WDTRACE" = "xyes" -o "x$WDTRACE" = "xauto" ; then
+    AC_CHECK_PROG([atalk_cv_have_dtrace], [dtrace], [yes], [no])
+    if test "x$atalk_cv_have_dtrace" = "xno" ; then
+      if test "x$WDTRACE" = "xyes" ; then
+        AC_MSG_FAILURE([dtrace requested but not found])
+      fi
+      WDTRACE="no"
+    else
+      WDTRACE="yes"
+    fi
+  fi
+
+  if test x"$WDTRACE" = x"yes" ; then
+    AC_DEFINE([WITH_DTRACE], [1], [dtrace probes])
+    DTRACE_LIBS=""
+    if test x"$this_os" = x"freebsd" ; then
+      DTRACE_LIBS="-lelf"
+    fi
+    AC_SUBST(DTRACE_LIBS)
+  fi
+  AM_CONDITIONAL(WITH_DTRACE, test "x$WDTRACE" = "xyes")
+])
+
+dnl Check for dbus-glib, for AFP stats
+AC_DEFUN([AC_NETATALK_DBUS_GLIB], [
+    atalk_cv_with_dbus=no
+    PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
+    PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1, have_dbus_glib=yes, have_dbus_glib=no)
+    PKG_CHECK_MODULES(DBUS_GTHREAD, gthread-2.0, have_dbus_gthread=yes, have_dbus_gthread=no)
+    AC_SUBST(DBUS_CFLAGS)
+    AC_SUBST(DBUS_LIBS)
+    AC_SUBST(DBUS_GLIB_CFLAGS)
+    AC_SUBST(DBUS_GLIB_LIBS)
+    AC_SUBST(DBUS_GTHREAD_CFLAGS)
+    AC_SUBST(DBUS_GTHREAD_LIBS)
+    if test x$have_dbus_glib = xyes -a x$have_dbus = xyes -a x$have_dbus_gthread = xyes ; then
+        saved_CFLAGS=$CFLAGS
+        saved_LIBS=$LIBS
+        CFLAGS="$CFLAGS $DBUS_GLIB_CFLAGS"
+        LIBS="$LIBS $DBUS_GLIB_LIBS"
+        AC_CHECK_FUNC([dbus_g_bus_get_private], [atalk_cv_with_dbus=yes], [atalk_cv_with_dbus=no])
+        CFLAGS="$saved_CFLAGS"
+        LIBS="$saved_LIBS"
+    fi
+    AM_CONDITIONAL(HAVE_DBUS_GLIB, test x$atalk_cv_with_dbus = xyes)
+
+    AC_ARG_WITH(
+        dbus-sysconf-dir,
+        [AS_HELP_STRING([--with-dbus-sysconf-dir=PATH],[Path to dbus system bus security configuration directory (default: ${sysconfdir}/dbus-1/system.d/)])],
+        ac_cv_dbus_sysdir=$withval,
+        ac_cv_dbus_sysdir='${sysconfdir}/dbus-1/system.d'
+    )
+
+    if test x$atalk_cv_with_dbus = xyes ; then
+        AC_DEFINE(HAVE_DBUS_GLIB, 1, [Define if support for dbus-glib was found])
+        DBUS_SYS_DIR="$ac_cv_dbus_sysdir"
+        AC_SUBST(DBUS_SYS_DIR)
+    fi
+])
+
 dnl Whether to enable developer build
 AC_DEFUN([AC_DEVELOPER], [
     AC_MSG_CHECKING([whether to enable developer build])
@@ -15,20 +127,26 @@ AC_DEFUN([AC_DEVELOPER], [
 
 dnl Whether to disable bundled libevent
 AC_DEFUN([AC_NETATALK_LIBEVENT], [
-    use_bundled_libevent=no
-    AC_MSG_CHECKING([whether to use bundled or installed libevent])
+    AC_MSG_CHECKING([whether to use bundled libevent])
+    AC_ARG_WITH(
+        libevent,
+        [AS_HELP_STRING([--with-libevent],[whether to use the bundled libevent (default: yes)])],
+        use_bundled_libevent=$withval,
+        use_bundled_libevent=yes
+    )
     AC_ARG_WITH(
         libevent-header,
         [AS_HELP_STRING([--with-libevent-header],[path to libevent header files])],
-        LIBEVENT_CFLAGS=-I$withval,
-        use_bundled_libevent=yes
+        [use_bundled_libevent=no; LIBEVENT_CFLAGS=-I$withval]
     )
     AC_ARG_WITH(
         libevent-lib,
-        [AS_HELP_STRING([--with-libevent-lib],[path to libevent header library])],
-        LIBEVENT_LDFLAGS=-L$withval,
-        use_bundled_libevent=yes
+        [AS_HELP_STRING([--with-libevent-lib],[path to libevent library])],
+        [use_bundled_libevent=no; LIBEVENT_LDFLAGS=-L$withval]
     )
+    if test x"$LIBEVENT_CFLAGS" = x"-Iyes" -o x"$LIBEVENT_LDFLAGS" = x"-Lyes" ; then
+        AC_MSG_ERROR([--with-libevent requires a path])
+    fi
     AC_MSG_RESULT([$use_bundled_libevent])
     if test x"$use_bundled_libevent" = x"yes" ; then
         AC_CONFIG_SUBDIRS([libevent])
@@ -56,13 +174,47 @@ AC_ARG_ENABLE(fhs,
                use_pam_so=yes
                AC_DEFINE(FHS_COMPATIBILITY, 1, [Define if you want compatibily with the FHS])
                AC_MSG_RESULT([yes])
+        atalk_cv_fhs_compat=yes
        else
                AC_MSG_RESULT([no])
+        atalk_cv_fhs_compat=no
        fi
        ],[
                AC_MSG_RESULT([no])
-       ]
-)])
+        atalk_cv_fhs_compat=no
+])])
+
+dnl netatalk lockfile path
+AC_DEFUN([AC_NETATALK_LOCKFILE], [
+    AC_MSG_CHECKING([netatalk lockfile path])
+    AC_ARG_WITH(
+        lockfile,
+        [AS_HELP_STRING([--with-lockfile=PATH],[Path of netatalk lockfile])],
+        ac_cv_netatalk_lock=$withval,
+        ac_cv_netatalk_lock=""
+    )
+    if test -z "$ac_cv_netatalk_lock" ; then
+        ac_cv_netatalk_lock=/var/spool/locks/netatalk
+        if test x"$atalk_cv_fhs_compat" = x"yes" ; then
+            ac_cv_netatalk_lock=/var/run/netatalk.pid
+        else
+            case "$host_os" in
+            *freebsd*)
+                ac_cv_netatalk_lock=/var/spool/lock/netatalk
+                ;;
+            *netbsd*|*openbsd*)
+                ac_cv_netatalk_lock=/var/run/netatalk.pid
+                ;;
+            *linux*)
+                ac_cv_netatalk_lock=/var/lock/netatalk
+                ;;
+            esac
+        fi
+    fi
+    AC_DEFINE_UNQUOTED(PATH_NETATALK_LOCK, ["$ac_cv_netatalk_lock"], [netatalk lockfile path])
+    AC_SUBST(PATH_NETATALK_LOCK, ["$ac_cv_netatalk_lock"])
+    AC_MSG_RESULT([$ac_cv_netatalk_lock])
+])
 
 dnl 64bit platform check
 AC_DEFUN([AC_NETATALK_64BIT_LIBS], [
@@ -245,7 +397,7 @@ AC_ARG_ENABLE(shell-check,
 )
 ])
 
-dnl Check for optional sysv initscript install
+dnl Check for optional initscript install
 AC_DEFUN([AC_NETATALK_INIT_STYLE], [
     AC_ARG_WITH(init-style,
                 [  --with-init-style       use OS specific init config [[redhat-sysv|redhat-systemd|suse-sysv|suse-systemd|gentoo|netbsd|debian|solaris|systemd]]],
@@ -257,36 +409,46 @@ AC_DEFUN([AC_NETATALK_INIT_STYLE], [
         ;;
     "redhat-sysv")
            AC_MSG_RESULT([enabling redhat-style sysv initscript support])
+           ac_cv_init_dir="/etc/rc.d/init.d"
            ;;
     "redhat-systemd")
            AC_MSG_RESULT([enabling redhat-style systemd support])
+           ac_cv_init_dir="/lib/systemd/system"
            ;;
     "suse")
            AC_MSG_ERROR([--with-init-style=suse is obsoleted. Use suse-sysv or suse-systemd.])
         ;;
     "suse-sysv")
            AC_MSG_RESULT([enabling suse-style sysv initscript support])
+           ac_cv_init_dir="/etc/init.d"
            ;;
     "suse-systemd")
            AC_MSG_RESULT([enabling suse-style systemd support (>=openSUSE12.1)])
+           ac_cv_init_dir="/lib/systemd/system"
            ;;
     "gentoo")
            AC_MSG_RESULT([enabling gentoo-style initscript support])
+           ac_cv_init_dir="/etc/init.d"
         ;;
     "netbsd")
            AC_MSG_RESULT([enabling netbsd-style initscript support])
+           ac_cv_init_dir="/etc/rc.d"
         ;;
     "debian")
            AC_MSG_RESULT([enabling debian-style initscript support])
+           ac_cv_init_dir="/etc/init.d"
         ;;
     "solaris")
            AC_MSG_RESULT([enabling solaris-style SMF support])
+           ac_cv_init_dir="/lib/svc/manifest/network/"
         ;;
     "systemd")
            AC_MSG_RESULT([enabling general systemd support])
+           ac_cv_init_dir="/lib/systemd/system"
         ;;
     "none")
            AC_MSG_RESULT([disabling init-style support])
+           ac_cv_init_dir="none"
         ;;
     *)
            AC_MSG_ERROR([illegal init-style])
@@ -301,6 +463,12 @@ AC_DEFUN([AC_NETATALK_INIT_STYLE], [
     AM_CONDITIONAL(USE_SYSTEMD, test x$init_style = xsystemd || test x$init_style = xredhat-systemd || test x$init_style = xsuse-systemd)
     AM_CONDITIONAL(USE_UNDEF, test x$init_style = xnone)
 
+    AC_ARG_WITH(init-dir,
+                [  --with-init-dir=PATH    path to OS specific init directory],
+                ac_cv_init_dir="$withval", ac_cv_init_dir="$ac_cv_init_dir"
+    )
+    INIT_DIR="$ac_cv_init_dir"
+    AC_SUBST(INIT_DIR, ["$ac_cv_init_dir"])
 ])
 
 dnl OS specific configuration
@@ -355,7 +523,7 @@ fi
 dnl ----- Linux specific -----
 if test x"$this_os" = "xlinux"; then 
        AC_MSG_RESULT([ * Linux specific configuration])
-       
+    AC_DEFINE(LINUX, 1, [OS is Linux]) 
        dnl ----- check if we need the quotactl wrapper
     AC_CHECK_HEADERS(linux/dqblk_xfs.h,,
                [AC_CHECK_HEADERS(linux/xqm.h linux/xfs_fs.h)
@@ -581,7 +749,7 @@ save_CFLAGS="$CFLAGS"
 save_LIBS="$LIBS"
 CFLAGS="$KRB5_CFLAGS"
 LIBS="$KRB5_LIBS"
-AC_CHECK_FUNCS([krb5_free_unparsed_name krb5_free_error_message])
+AC_CHECK_FUNCS([krb5_free_unparsed_name krb5_free_error_message krb5_free_keytab_entry_contents krb5_kt_free_entry])
 CFLAGS="$save_CFLAGS"
 LIBS="$save_LIBS"
 ])
@@ -649,7 +817,7 @@ AC_SUBST(LDAP_CFLAGS)
 AC_SUBST(LDAP_LDFLAGS)
 AC_SUBST(LDAP_LIBS)
 CFLAGS="$save_CFLAGS"
-LDLFLAGS="$save_LDLFLAGS"
+LDFLAGS="$save_LDFLAGS"
 LIBS="$save_LIBS"
 ])
 
index 6bc40cbad841562161bf2c1a6a1f3cd776cdb5e4..609c92dcae18885775afd8b744b1d6a5a73f9b73 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: pam-check.m4,v 1.6 2010-01-11 13:06:02 franklahm Exp $
 dnl PAM finding macro
 
 AC_DEFUN([AC_NETATALK_PATH_PAM], [
@@ -98,6 +97,13 @@ AC_DEFUN([AC_NETATALK_PATH_PAM], [
            PAM_ACCOUNT=system
            PAM_PASSWORD=system
            PAM_SESSION=system
+        dnl Solaris 11+
+        elif test -f "$pampath/other" ; then
+           PAM_DIRECTIVE=include
+           PAM_AUTH=${PAMDIR}etc/pam.d/other
+           PAM_ACCOUNT=${PAMDIR}etc/pam.d/other
+           PAM_PASSWORD=${PAMDIR}etc/pam.d/other
+           PAM_SESSION=${PAMDIR}etc/pam.d/other
         dnl Fallback
         else
            PAM_DIRECTIVE=required
@@ -132,6 +138,15 @@ AC_DEFUN([AC_NETATALK_PATH_PAM], [
            AC_DEFINE(USE_PAM, 1, [Define to enable PAM support])
        fi
 
+    AC_ARG_WITH(
+        pam-confdir,
+        [AS_HELP_STRING([--with-pam-confdir=PATH],[Path to PAM config dir (default: ${sysconfdir}/pam.d)])],
+        ac_cv_pamdir=$withval,
+        ac_cv_pamdir='${sysconfdir}/pam.d'
+    )
+
+    PAMDIR="$ac_cv_pamdir"
+
     LIB_REMOVE_USR_LIB(PAM_LIBS)
     CFLAGS_REMOVE_USR_INCLUDE(PAM_CFLAGS)
        AC_SUBST(PAMDIR)
index 7ad755b973e52aaf02263c0896337acc54f30eaa..6ae7ea6f901a1e174fef04a88ccca740fa6a944d 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of Perl
-dnl $Id: perl-check.m4,v 1.4 2002-03-12 11:03:49 srittau Exp $
 
 AC_DEFUN([AC_PROG_PERL], [
        AC_REQUIRE([AC_EXEEXT])dnl
index 3f47b85f37ef7f3ee45c4a107e151d7868349e51..75d5c9538553f708b3a3cc89c1d17a4e9f26c28c 100644 (file)
@@ -1,5 +1,4 @@
 dnl Autoconf macro to check for the existence of ps
-dnl $Id: ps-check.m4,v 1.2 2002-02-14 18:02:04 jmarcus Exp $
 
 AC_DEFUN([AC_PROG_PS], [
 AC_REQUIRE([AC_EXEEXT])dnl
index ad135080e91f0facd1610c26190561020741f54a..e2e145e616ed15da19fec87d1729c7ed211f9666 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: quota-check.m4,v 1.6 2005-07-20 23:58:21 didg Exp $
 dnl Autoconf macro to check for quota support
 dnl FIXME: This is in now way complete.
 
index 8d9c1b6c66650e5ca8f8f337057b8a229c69cccc..8a58142be879c1a8402dce97797e67feadee1ab6 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: ssl-check.m4,v 1.14 2008-11-22 12:07:26 didg Exp $
 dnl Autoconf macro to check for SSL or OpenSSL
 
 AC_DEFUN([AC_NETATALK_CRYPT], [
index 94e84ac94ce565f2d103d882c6f1cbc7397ea3e5..ba8cde05a352b88523144b8a2d0f8fca5ef279da 100644 (file)
@@ -53,14 +53,29 @@ dnl AC_MSG_RESULT([         Samba sharemode interop: $neta_cv_have_smbshmd])
        AC_MSG_RESULT([         ACL support:             $with_acl_support])
        AC_MSG_RESULT([         Kerberos support:        $with_kerberos])
        AC_MSG_RESULT([         LDAP support:            $netatalk_cv_ldap])
-       if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
+       AC_MSG_RESULT([         dbus support:            $atalk_cv_with_dbus])
+       AC_MSG_RESULT([         dtrace probes:           $WDTRACE])
+       AC_MSG_RESULT([    Paths:])
+       AC_MSG_RESULT([         Netatalk lockfile:       $ac_cv_netatalk_lock])
+       if test "x$init_style" != x"none"; then
+               AC_MSG_RESULT([         init directory:          $ac_cv_init_dir])
+       fi
+       if test x"$atalk_cv_with_dbus" = x"yes"; then
+               AC_MSG_RESULT([         dbus system directory:   $ac_cv_dbus_sysdir])
+       fi
+       if test x"$use_pam_so" = x"yes"; then
+          if test x"$netatalk_cv_install_pam" = x"yes"; then
+               AC_MSG_RESULT([         pam config directory:    $ac_cv_pamdir])
+          else
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
                AC_MSG_WARN([ cannot be installed. Please install the config/netatalk.pamd file manually.])
                AC_MSG_WARN([ If you're running Solaris or BSD you'll have to edit /etc/pam.conf to get PAM working.])
                AC_MSG_WARN([ You can also re-run configure and specify --without-pam to disable PAM support.])
+          fi
        fi
-
+       AC_MSG_RESULT([    Documentation:])
+       AC_MSG_RESULT([         Docbook:                 $XSLTPROC_WORKS])
 ])
 
 
@@ -69,8 +84,11 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        dnl # Display summary of libraries detected
 
        AC_MSG_RESULT([Using libraries:])
-       AC_MSG_RESULT([    LIBS = $LIBS])
-       AC_MSG_RESULT([    CFLAGS = $CFLAGS])
+       AC_MSG_RESULT([    LIBS           = $LIBS])
+       AC_MSG_RESULT([    CFLAGS         = $CFLAGS])
+       AC_MSG_RESULT([    PTHREADS:])
+       AC_MSG_RESULT([        LIBS   = $PTHREAD_LIBS])
+       AC_MSG_RESULT([        CFLAGS = $PTHREAD_CFLAGS])
        if test x"$neta_cv_have_openssl" = x"yes"; then
                AC_MSG_RESULT([    SSL:])
                AC_MSG_RESULT([        LIBS   = $SSL_LIBS])
@@ -113,7 +131,14 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        fi
        if test x"$netatalk_cv_ldap" = x"yes"; then
                AC_MSG_RESULT([    LDAP:])
-               AC_MSG_RESULT([        LIBS   = $LDAP_LDLFLAGS $LDAP_LIBS])
+               AC_MSG_RESULT([        LIBS   = $LDAP_LDFLAGS $LDAP_LIBS])
                AC_MSG_RESULT([        CFLAGS = $LDAP_CFLAGS])
        fi
+    AC_MSG_RESULT([    LIBEVENT:])
+    if test x"$use_bundled_libevent" = x"yes"; then
+               AC_MSG_RESULT([        bundled])
+    else
+               AC_MSG_RESULT([        LIBS   = $LIBEVENT_CFLAGS])
+               AC_MSG_RESULT([        CFLAGS = $LIBEVENT_LDFLAGS])
+    fi
 ])
index 55b0f8c179428c464634cd6f5aa601b6f6521a9f..76b75a09cd05a85d633c1bdfd20645d83cb86f82 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: tcp-wrappers.m4,v 1.4 2008-08-11 20:44:03 didg Exp $
 
 AC_DEFUN([AC_NETATALK_TCP_WRAPPERS], [
        check=maybe
index 895a5e778b39b68094b7bf7771c67cf63c08dec9..8f1b0d992ec94e80ad60fd4ba1688d5d9900b917 100644 (file)
@@ -1,4 +1,3 @@
 Makefile
 Makefile.in
-.gitignore
 *.o
index c767a967abd49b2fcfcba5f0d80066eb03c9b2a1..0ac9461e8a4ab22a449d068e52155a4879735fd3 100644 (file)
@@ -1,11 +1,3 @@
 Makefile
 Makefile.in
-apple_cp.1
-apple_dump.1
-apple_mv.1
-apple_rm.1
-asip-status.pl.1
-afpldaptest.1
-uniconv.1
-.gitignore
-*.o
+*.1
index fe81a7d762bf3f8831066049d9523bbeef6dd1ee..07f4f9a71dd061aace628bc2598b0847403aef19 100644 (file)
@@ -1,27 +1,15 @@
 # Makefile.am for man/man1/
 
-pkgconfdir = @PKGCONFDIR@
-
-SUFFIXES= .tmpl .
-
-.tmpl:
-       sed -e s@:SBINDIR:@${sbindir}@ \
-           -e s@:BINDIR:@${bindir}@ \
-           -e s@:ETCDIR:@${pkgconfdir}@ \
-           -e s@:LIBDIR:@${libdir}@ \
-           -e s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@ \
-           <$< >$@
-
-GENERATED_MANS = uniconv.1 asip-status.pl.1 afpldaptest.1
-TEMPLATE_FILES = uniconv.1.tmpl asip-status.pl.1.tmpl afpldaptest.1.tmpl
-
-NONGENERATED_MANS      =       ad.1 \
-                               afppasswd.1 \
-                               apple_dump.1 \
-                               dbd.1 \
-                               macusers.1 \
-                               netatalk-config.1
-
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
-CLEANFILES = $(GENERATED_MANS)
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+man_MANS = \
+       ad.1 \
+       afpldaptest.1 \
+       afppasswd.1 \
+       afpstats.1 \
+       apple_dump.1 \
+       asip-status.pl.1 \
+       dbd.1 \
+       macusers.1 \
+       netatalk-config.1 \
+       uniconv.1
+
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man1/ad.1 b/man/man1/ad.1
deleted file mode 100644 (file)
index 4d85e8b..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-'\" t
-.\"     Title: ad
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Sep 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AD" "1" "02 Sep 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-ad \- Netatalk compatible UNIX file utility suite\&.
-.SH "SYNOPSIS"
-.HP \w'\fBad\fR\ 'u
-\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
-.HP \w'\fBad\fR\ 'u
-\fBad\fR {\-v\ |\ \-\-version}
-.SH "DESCRIPTION"
-.PP
-\fBad\fR
-is a UNIX file utlity suite with Netatalk compatibity\&. AppleDouble
-files in
-\&.AppleDouble
-directories and the CNID databases are updated as appropiate\&.
-.SH "AVAILABLE COMMANDS"
-.HP \w'\fBad\ ls\fR\ 'u
-\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
-.PP
-List files and directories\&.
-.HP \w'\fBad\ cp\fR\ 'u
-\fBad cp\fR [\-aipvf] {src_file} {dst_file}
-.HP \w'\fBad\ cp\ \-R\fR\ 'u
-\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
-.PP
-Copy files and directories\&.
-.HP \w'\fBad\ mv\fR\ 'u
-\fBad mv\fR [\-finv] {src_file} {dst_file}
-.HP \w'\fBad\ mv\fR\ 'u
-\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
-.PP
-Move files and directories\&.
-.HP \w'\fBad\ rm\fR\ 'u
-\fBad rm\fR [\-Rv] {file|directory}
-.HP \w'\fBad\ \-v|\-\-version\fR\ 'u
-\fBad \-v|\-\-version\fR
-.PP
-Show version\&.
-.SH "AD LS"
-.PP
-List files and directories\&. Options:
-.PP
-\-d
-.RS 4
-Directories are listed as plain files
-.RE
-.PP
-\-R
-.RS 4
-list subdirectories recursively
-.RE
-.PP
-\-l
-.RS 4
-Long output, list AFP info
-.RE
-.PP
-\-u
-.RS 4
-List UNIX info
-.RE
-.PP
-\fILong output description\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
-
-FinderFlags (valid for (f)iles and/or (d)irectories):
-
-  d = On Desktop                      (f/d)
-  e = Hidden extension                (f/d)
-  m = Shared (can run multiple times) (f)
-  n = No INIT resources               (f)
-  i = Inited                          (f/d)
-  c = Custom icon                     (f/d)
-  t = Stationery                      (f)
-  s = Name locked                     (f/d)
-  b = Bundle                          (f/d)
-  v = Invisible                       (f/d)
-  a = Alias file                      (f/d)
-
-AFP Attributes:
-
-  y = System                          (f/d)
-  w = No write                        (f)
-  p = Needs backup                    (f/d)
-  r = No rename                       (f/d)
-  l = No delete                       (f/d)
-  o = No copy                         (f)
-
-Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
-.fi
-.if n \{\
-.RE
-.\}
-.SH "AD CP"
-.PP
-Copy files and directories\&.
-.PP
-In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
-.PP
-Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targetting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
-.PP
-Options:
-.PP
-\-a
-.RS 4
-Archive mode\&. Same as \-Rp\&.
-.RE
-.PP
-\-f
-.RS 4
-For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
-.RE
-.PP
-\-i
-.RS 4
-Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \'y\' or \'Y\', the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
-.RE
-.PP
-\-n
-.RS 4
-Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
-.RE
-.PP
-\-p
-.RS 4
-Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
-.RE
-.PP
-\-R
-.RS 4
-If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
-.RE
-.PP
-\-v
-.RS 4
-Cause cp to be verbose, showing files as they are copied\&.
-.RE
-.PP
-\-x
-.RS 4
-File system mount points are not traversed\&.
-.RE
-.SH "AD MV"
-.PP
-Move files and directories\&.
-.PP
-Move files around within an AFP volume, updating the CNID database as needed\&. If either:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-source or destination is not an AFP volume
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-source AFP volume != destination AFP volume
-.RE
-.sp
-.RE
-the files are copied and removed from the source\&.
-.PP
-Options:
-.PP
-\-f
-.RS 4
-Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
-.RE
-.PP
-\-i
-.RS 4
-Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\' or `Y\', the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
-.RE
-.PP
-\-n
-.RS 4
-Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
-.RE
-.PP
-\-v
-.RS 4
-Cause mv to be verbose, showing files after they are moved\&.
-.RE
-.SH "AD RM"
-.PP
-Remove files and directories\&.
-.PP
-The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
-.PP
-The options are as follows:
-.PP
-\-R
-.RS 4
-Attempt to remove the file hierarchy rooted in each file argument\&.
-.RE
-.PP
-\-v
-.RS 4
-Be verbose when deleting files, showing them as they are removed\&.
-.RE
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBdbd\fR(1),
-\fBapple_dump\fR(1)\&.
diff --git a/man/man1/ad.1.in b/man/man1/ad.1.in
new file mode 100644 (file)
index 0000000..1a807cf
--- /dev/null
@@ -0,0 +1,250 @@
+'\" t
+.\"     Title: ad
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 02 Sep 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AD" "1" "02 Sep 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+ad \- Netatalk compatible UNIX file utility suite\&.
+.SH "SYNOPSIS"
+.HP \w'\fBad\fR\ 'u
+\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
+.HP \w'\fBad\fR\ 'u
+\fBad\fR {\-v\ |\ \-\-version}
+.SH "DESCRIPTION"
+.PP
+\fBad\fR
+is a UNIX file utility suite with Netatalk compatibility\&. AppleDouble
+files in
+\&.AppleDouble
+directories and the CNID databases are updated as appropriate\&.
+.SH "AVAILABLE COMMANDS"
+.HP \w'\fBad\ ls\fR\ 'u
+\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
+.PP
+List files and directories\&.
+.HP \w'\fBad\ cp\fR\ 'u
+\fBad cp\fR [\-aipvf] {src_file} {dst_file}
+.HP \w'\fBad\ cp\ \-R\fR\ 'u
+\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Copy files and directories\&.
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file} {dst_file}
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Move files and directories\&.
+.HP \w'\fBad\ rm\fR\ 'u
+\fBad rm\fR [\-Rv] {file|directory}
+.HP \w'\fBad\ \-v|\-\-version\fR\ 'u
+\fBad \-v|\-\-version\fR
+.PP
+Show version\&.
+.SH "AD LS"
+.PP
+List files and directories\&. Options:
+.PP
+\-d
+.RS 4
+Directories are listed as plain files
+.RE
+.PP
+\-R
+.RS 4
+list subdirectories recursively
+.RE
+.PP
+\-l
+.RS 4
+Long output, list AFP info
+.RE
+.PP
+\-u
+.RS 4
+List UNIX info
+.RE
+.PP
+\fILong output description\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
+
+  d = On Desktop                      (f/d)
+  e = Hidden extension                (f/d)
+  m = Shared (can run multiple times) (f)
+  n = No INIT resources               (f)
+  i = Inited                          (f/d)
+  c = Custom icon                     (f/d)
+  t = Stationery                      (f)
+  s = Name locked                     (f/d)
+  b = Bundle                          (f/d)
+  v = Invisible                       (f/d)
+  a = Alias file                      (f/d)
+
+AFP Attributes:
+
+  y = System                          (f/d)
+  w = No write                        (f)
+  p = Needs backup                    (f/d)
+  r = No rename                       (f/d)
+  l = No delete                       (f/d)
+  o = No copy                         (f)
+
+Note: any letter appearing in uppercase means the flag is set but it\*(Aqs a directory for which the flag is not allowed\&.
+.fi
+.if n \{\
+.RE
+.\}
+.SH "AD CP"
+.PP
+Copy files and directories\&.
+.PP
+In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
+.PP
+Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targeting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
+.PP
+Options:
+.PP
+\-a
+.RS 4
+Archive mode\&. Same as \-Rp\&.
+.RE
+.PP
+\-f
+.RS 4
+For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \*(Aqy\*(Aq or \*(AqY\*(Aq, the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-p
+.RS 4
+Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
+.RE
+.PP
+\-R
+.RS 4
+If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
+.RE
+.PP
+\-v
+.RS 4
+Cause cp to be verbose, showing files as they are copied\&.
+.RE
+.PP
+\-x
+.RS 4
+File system mount points are not traversed\&.
+.RE
+.SH "AD MV"
+.PP
+Move files and directories\&.
+.PP
+Move files around within an AFP volume, updating the CNID database as needed\&. If either:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source or destination is not an AFP volume
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source AFP volume != destination AFP volume
+.RE
+.sp
+the files are copied and removed from the source\&.
+.PP
+Options:
+.PP
+\-f
+.RS 4
+Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\*(Aq or `Y\*(Aq, the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-v
+.RS 4
+Cause mv to be verbose, showing files after they are moved\&.
+.RE
+.SH "AD RM"
+.PP
+Remove files and directories\&.
+.PP
+The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
+.PP
+The options are as follows:
+.PP
+\-R
+.RS 4
+Attempt to remove the file hierarchy rooted in each file argument\&.
+.RE
+.PP
+\-v
+.RS 4
+Be verbose when deleting files, showing them as they are removed\&.
+.RE
+.SH "REPORTING BUGS"
+.PP
+Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
+.SH "SEE ALSO"
+.PP
+\fBdbd\fR(1),
+\fBapple_dump\fR(1)\&.
diff --git a/man/man1/afpldaptest.1.in b/man/man1/afpldaptest.1.in
new file mode 100644 (file)
index 0000000..94d6bd5
--- /dev/null
@@ -0,0 +1,67 @@
+'\" t
+.\"     Title: afpldaptest
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPLDAPTEST" "1" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpldaptest \- Syntactically check ldap parameters in afp\&.conf
+.SH "SYNOPSIS"
+.HP \w'\fBafpldaptest\fR\fB\fR\fBafpldaptest\fR\fB\fR\ 'u
+\fBafpldaptest\fR\fB\fR {\-u\ \fIUSER\fR | \-g\ \fIGROUP\fR | \-i\ \fIUUID\fR}
+.br
+\fBafpldaptest\fR\fB\fR {\-h | \-? | \-:}
+.SH "DESCRIPTION"
+.PP
+\fBafpldaptest\fR
+is a simple command to syntactically check ldap parameters in @pkgconfdir@/afp\&.conf\&.
+.SH "OPTIONS"
+.PP
+\fB\-u\fR \fIUSER\fR
+.RS 4
+Show uuid for
+\fIUSER\fR\&.
+.RE
+.PP
+\fB\-g\fR \fIGROUP\fR
+.RS 4
+Show uuid for
+\fIGROUP\fR\&.
+.RE
+.PP
+\fB\-i\fR \fIUUID\fR
+.RS 4
+Show user, group or local\-uuid for
+\fIUUID\fR\&.
+.RE
+.PP
+\fB\-h, \-?, \-:\fR
+.RS 4
+Show the help and exit\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5)
diff --git a/man/man1/afpldaptest.1.tmpl b/man/man1/afpldaptest.1.tmpl
deleted file mode 100644 (file)
index 1d4fbd7..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-'\" t
-.\"     Title: afpldaptest
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPLDAPTEST" "1" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afpldaptest \- Syntactically check ldap parameters in afp\&.conf
-.SH "SYNOPSIS"
-.HP \w'\fBafpldaptest\fR\fB\fR\fBafpldaptest\fR\fB\fR\ 'u
-\fBafpldaptest\fR\fB\fR {\-u\ \fIUSER\fR | \-g\ \fIGROUP\fR | \-i\ \fIUUID\fR}
-.br
-\fBafpldaptest\fR\fB\fR {\-h | \-? | \-:}
-.SH "DESCRIPTION"
-.PP
-\fBafpldaptest\fR
-is a simple command to syntactically check ldap parameters in :ETCDIR:/afp\&.conf\&.
-.SH "OPTIONS"
-.PP
-\fB\-u\fR \fIUSER\fR
-.RS 4
-Show uuid for
-\fIUSER\fR\&.
-.RE
-.PP
-\fB\-g\fR \fIGROUP\fR
-.RS 4
-Show uuid for
-\fIGROUP\fR\&.
-.RE
-.PP
-\fB\-i\fR \fIUUID\fR
-.RS 4
-Show user, group or local\-uuid for
-\fIUUID\fR\&.
-.RE
-.PP
-\fB\-h, \-?, \-:\fR
-.RS 4
-Show the help and exit\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafp.conf\fR(5)
diff --git a/man/man1/afppasswd.1 b/man/man1/afppasswd.1
deleted file mode 100644 (file)
index 860eb69..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-'\" t
-.\"     Title: afppasswd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPPASSWD" "1" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afppasswd \- netatalk password maintenance utility
-.SH "SYNOPSIS"
-.HP \w'\fBafppasswd\fR\fB\fR\fB\fR\ 'u
-\fBafppasswd\fR\fB\fR\fB\fR [\-acfn] [\-p\ \fIpasswd\fR\ \fIfile\fR] [\-u\ \fIminimum\fR\ \fIuid\fR]
-.SH "DESCRIPTION"
-.PP
-\fBafppasswd\fR
-allows the maintenance of afppasswd files created by netatalk for use by the uams_randnum\&.so UAM (providing the "Randnum exchange" and "2\-Way Randnum exchange" User Authentication Modules)\&.
-.PP
-\fBafppasswd\fR
-can either be called by root with parameters, or can be called by local system users with no parameters to change their AFP passwords\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" and "DHX2" UAM instead\&. Please compare with the
-Authentication chapter
-inside Netatalk\'s documentation\&.
-.sp .5v
-.RE
-.SH "EXAMPLE"
-.PP
-Local user changing their own password:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example% \fBafppasswd\fR
-Enter NEW AFP password: \fB(hidden)\fR
-Enter NEW AFP password again: \fB(hidden)\fR
-afppasswd: updated password\&.
-.fi
-.if n \{\
-.RE
-.\}
-.SH "OPTIONS"
-.PP
-\fB\-a\fR
-.RS 4
-Add a new user to the
-\fBafppasswd\fR
-file\&.
-.RE
-.PP
-\fB\-c\fR
-.RS 4
-Create and/or initialize
-\fBafppasswd\fR
-file or specific user\&.
-.RE
-.PP
-\fB\-f\fR
-.RS 4
-Force the current action\&.
-.RE
-.PP
-\fB\-p\fR\fI path\fR
-.RS 4
-Path to
-\fBafppasswd\fR
-file\&.
-.RE
-.PP
-\fB\-n\fR
-.RS 4
-If cracklib support is built into
-\fBnetatalk\fR
-this option will cause cracklib checking to be disabled, if the superuser does not want to have the password run against the cracklib dictionary\&.
-.RE
-.PP
-\fB\-u\fR\fI minimum uid\fR
-.RS 4
-This is the minimum
-\fIuser id\fR
-(uid) that
-\fBafppasswd\fR
-will use when creating users\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5)\&.
diff --git a/man/man1/afppasswd.1.in b/man/man1/afppasswd.1.in
new file mode 100644 (file)
index 0000000..6acce22
--- /dev/null
@@ -0,0 +1,122 @@
+'\" t
+.\"     Title: afppasswd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPPASSWD" "1" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afppasswd \- netatalk password maintenance utility
+.SH "SYNOPSIS"
+.HP \w'\fBafppasswd\fR\fB\fR\fB\fR\ 'u
+\fBafppasswd\fR\fB\fR\fB\fR [\-acfn] [\-p\ \fIpasswd\fR\ \fIfile\fR] [\-u\ \fIminimum\fR\ \fIuid\fR]
+.SH "DESCRIPTION"
+.PP
+\fBafppasswd\fR
+allows the maintenance of afppasswd files created by netatalk for use by the uams_randnum\&.so UAM (providing the "Randnum exchange" and "2\-Way Randnum exchange" User Authentication Modules)\&.
+.PP
+\fBafppasswd\fR
+can either be called by root with parameters, or can be called by local system users with no parameters to change their AFP passwords\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" and "DHX2" UAM instead\&. Please compare with the
+Authentication chapter
+inside Netatalk\*(Aqs documentation\&.
+.sp .5v
+.RE
+.SH "EXAMPLE"
+.PP
+Local user changing their own password:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example% \fBafppasswd\fR
+Enter NEW AFP password: \fB(hidden)\fR
+Enter NEW AFP password again: \fB(hidden)\fR
+afppasswd: updated password\&.
+.fi
+.if n \{\
+.RE
+.\}
+.SH "OPTIONS"
+.PP
+\fB\-a\fR
+.RS 4
+Add a new user to the
+\fBafppasswd\fR
+file\&.
+.RE
+.PP
+\fB\-c\fR
+.RS 4
+Create and/or initialize
+\fBafppasswd\fR
+file or specific user\&.
+.RE
+.PP
+\fB\-f\fR
+.RS 4
+Force the current action\&.
+.RE
+.PP
+\fB\-p\fR\fI path\fR
+.RS 4
+Path to
+\fBafppasswd\fR
+file\&.
+.RE
+.PP
+\fB\-n\fR
+.RS 4
+If cracklib support is built into
+\fBnetatalk\fR
+this option will cause cracklib checking to be disabled, if the superuser does not want to have the password run against the cracklib dictionary\&.
+.RE
+.PP
+\fB\-u\fR\fI minimum uid\fR
+.RS 4
+This is the minimum
+\fIuser id\fR
+(uid) that
+\fBafppasswd\fR
+will use when creating users\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5)\&.
diff --git a/man/man1/afpstats.1.in b/man/man1/afpstats.1.in
new file mode 100644 (file)
index 0000000..16dc0b1
--- /dev/null
@@ -0,0 +1,50 @@
+'\" t
+.\"     Title: afpstats
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 24 Mar 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPSTATS" "1" "24 Mar 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpstats \- List AFP statistics
+.SH "SYNOPSIS"
+.HP \w'\fBafpstats\fR\fB\fR\ 'u
+\fBafpstats\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+\fBafpstats\fR
+list AFP statistics via D\-Bus IPC\&.
+.SH "NOTE"
+.PP
+\fBafpd\fR
+must support D\-Bus\&. Check it by "\fBafpd \-V\fR"\&.
+.PP
+"\fBafpstats = yes\fR" must be set in
+@pkgconfdir@/afp\&.conf\&.
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBdbus-daemon\fR(1)
diff --git a/man/man1/apple_dump.1 b/man/man1/apple_dump.1
deleted file mode 100644 (file)
index 37e9f6e..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-'\" t
-.\"     Title: apple_dump
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 16 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "APPLE_DUMP" "1" "16 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_dump \- Dump AppleSingle/AppleDouble format data
-.SH "SYNOPSIS"
-.HP \w'\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\ 'u
-\fBapple_dump\fR\fB\fR [\-a] [\fIFILE\fR | \fIDIR\fR]
-.br
-\fBapple_dump\fR\fB\fR \-e \fIFILE\fR | \fIDIR\fR 
-.br
-\fBapple_dump\fR\fB\fR \-f [\fIFILE\fR]
-.br
-\fBapple_dump\fR\fB\fR \-d [\fIFILE\fR]
-.br
-\fBapple_dump\fR\fB\fR \-h | \-help | \-\-help 
-.br
-\fBapple_dump\fR\fB\fR \-v | \-version | \-\-version 
-.SH "DESCRIPTION"
-.PP
-\fBapple_dump\fR
-is a perl script to dump AppleSingle/AppleDouble format data\&.
-.PP
-This script can dump various AppleSingle/AppleDouble data created by mailer, archiver, Mac OS X, Netatalk and so on\&.
-.PP
-With no
-\fIFILE\fR|\fIDIR\fR, or when
-\fIFILE\fR|\fIDIR\fR
-is \-, read standard input\&.
-.SH "OPTIONS"
-.PP
-\fB\-a\fR [\fIFILE\fR|\fIDIR\fR]
-.RS 4
-This is default\&. Dump a AppleSingle/AppleDouble file for
-\fIFILE\fR
-or
-\fIDIR\fR
-automatically\&. If FILE is not AppleSingle/AppleDouble format, look for extended attribute,
-\fI\&.AppleDouble/FILE\fR
-and
-\fI\&._FILE\fR\&. If
-\fIDIR\fR, look for extended attribute,
-\fIDIR/\&.AppleDouble/\&.Parent\fR
-and
-\fI\&._DIR\fR\&.
-.RE
-.PP
-\fB\-e\fR \fIFILE\fR|\fIDIR\fR
-.RS 4
-Dump extended attribute of\fIFILE\fR
-or
-\fIDIR\fR\&.
-.RE
-.PP
-\fB\-f\fR [\fIFILE\fR]
-.RS 4
-Dump
-\fIFILE\fR\&. Assume FinderInfo to be FileInfo\&.
-.RE
-.PP
-\fB\-d\fR [\fIFILE\fR]
-.RS 4
-Dump
-\fIFILE\fR\&. Assume FinderInfo to be DirInfo\&.
-.RE
-.PP
-\fB\-h, \-help, \-\-help\fR
-.RS 4
-Display the help and exit
-.RE
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version and exit
-.RE
-.SH "NOTE"
-.PP
-There is no way to detect whether FinderInfo is FileInfo or DirInfo\&. By default, apple_dump examins whether file or directory, a parent directory is \&.AppleDouble, filename is \&._*, filename is \&.Parent, and so on\&.
-.PP
-If setting option \-e, \-f or \-d, assume FinderInfo and doesn\'t look for another file\&.
-.SH "SEE ALSO"
-.PP
-\fBad\fR(1),
-\fBgetfattr\fR(1),
-\fBattr\fR(1),
-\fBrunat\fR(1),
-\fBgetextattr\fR(8),
-\fBlsextattr\fR(8)
diff --git a/man/man1/apple_dump.1.in b/man/man1/apple_dump.1.in
new file mode 100644 (file)
index 0000000..c9d7ff6
--- /dev/null
@@ -0,0 +1,114 @@
+'\" t
+.\"     Title: apple_dump
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 16 Jul 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "APPLE_DUMP" "1" "16 Jul 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+apple_dump \- Dump AppleSingle/AppleDouble format data
+.SH "SYNOPSIS"
+.HP \w'\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\fBapple_dump\fR\fB\fR\ 'u
+\fBapple_dump\fR\fB\fR [\-a] [\fIFILE\fR | \fIDIR\fR]
+.br
+\fBapple_dump\fR\fB\fR \-e \fIFILE\fR | \fIDIR\fR 
+.br
+\fBapple_dump\fR\fB\fR \-f [\fIFILE\fR]
+.br
+\fBapple_dump\fR\fB\fR \-d [\fIFILE\fR]
+.br
+\fBapple_dump\fR\fB\fR \-h | \-help | \-\-help 
+.br
+\fBapple_dump\fR\fB\fR \-v | \-version | \-\-version 
+.SH "DESCRIPTION"
+.PP
+\fBapple_dump\fR
+is a perl script to dump AppleSingle/AppleDouble format data\&.
+.PP
+This script can dump various AppleSingle/AppleDouble data created by mailer, archiver, Mac OS X, Netatalk and so on\&.
+.PP
+With no
+\fIFILE\fR|\fIDIR\fR, or when
+\fIFILE\fR|\fIDIR\fR
+is \-, read standard input\&.
+.SH "OPTIONS"
+.PP
+\fB\-a\fR [\fIFILE\fR|\fIDIR\fR]
+.RS 4
+This is default\&. Dump a AppleSingle/AppleDouble file for
+\fIFILE\fR
+or
+\fIDIR\fR
+automatically\&. If FILE is not AppleSingle/AppleDouble format, look for extended attribute,
+\fI\&.AppleDouble/FILE\fR
+and
+\fI\&._FILE\fR\&. If
+\fIDIR\fR, look for extended attribute,
+\fIDIR/\&.AppleDouble/\&.Parent\fR
+and
+\fI\&._DIR\fR\&.
+.RE
+.PP
+\fB\-e\fR \fIFILE\fR|\fIDIR\fR
+.RS 4
+Dump extended attribute of\fIFILE\fR
+or
+\fIDIR\fR\&.
+.RE
+.PP
+\fB\-f\fR [\fIFILE\fR]
+.RS 4
+Dump
+\fIFILE\fR\&. Assume FinderInfo to be FileInfo\&.
+.RE
+.PP
+\fB\-d\fR [\fIFILE\fR]
+.RS 4
+Dump
+\fIFILE\fR\&. Assume FinderInfo to be DirInfo\&.
+.RE
+.PP
+\fB\-h, \-help, \-\-help\fR
+.RS 4
+Display the help and exit
+.RE
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version and exit
+.RE
+.SH "NOTE"
+.PP
+There is no way to detect whether FinderInfo is FileInfo or DirInfo\&. By default, apple_dump examines whether file or directory, a parent directory is \&.AppleDouble, filename is \&._*, filename is \&.Parent, and so on\&.
+.PP
+If setting option \-e, \-f or \-d, assume FinderInfo and doesn\*(Aqt look for another file\&.
+.SH "SEE ALSO"
+.PP
+\fBad\fR(1),
+\fBgetfattr\fR(1),
+\fBattr\fR(1),
+\fBrunat\fR(1),
+\fBgetextattr\fR(8),
+\fBlsextattr\fR(8)
diff --git a/man/man1/asip-status.pl.1.in b/man/man1/asip-status.pl.1.in
new file mode 100644 (file)
index 0000000..2963617
--- /dev/null
@@ -0,0 +1,135 @@
+'\" t
+.\"     Title: asip-status.pl
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 24 Jul 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "ASIP\-STATUS\&.PL" "1" "24 Jul 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+asip-status.pl \- Queries AFP servers for their capabilities
+.SH "SYNOPSIS"
+.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
+\fBasip\-status\&.pl\fR\fB\fR [\-d] [\-i] [\-x] HOSTNAME[:PORT]
+
+.br
+.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
+\fBasip\-status\&.pl\fR\fB\fR \-v | \-version | \-\-version 
+.SH "DESCRIPTION"
+.PP
+\fBasip\-status\&.pl\fR
+is a perl script that sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and displays the results, namely "Machine type", the server\*(Aqs name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
+.PP
+When you don\*(Aqt supply :PORT, then the default AFP port, 548, will be used\&.
+.SH "OPTIONS"
+.PP
+\fB\-d\fR
+.RS 4
+Enable debug output\&.
+.RE
+.PP
+\fB\-i\fR
+.RS 4
+Show icon if it exists\&.
+.RE
+.PP
+\fB\-x\fR
+.RS 4
+Enable hex dump output\&.
+.RE
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version\&.
+.RE
+.SH "EXAMPLES"
+.PP
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBasip\-status\&.pl\fR 192\&.168\&.1\&.15
+AFP reply from 192\&.168\&.1\&.15:548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Macintosh
+AFP versions: AFPVersion 1\&.1,AFPVersion 2\&.0,AFPVersion 2\&.1,AFP2\&.2
+UAMs: Cleartxt passwrd,Randnum exchange,2\-Way Randnum exchange
+Volume Icon & Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsChgPwd
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSuperClient
+Server name: bookchan
+Signature:
+04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  \&.\&.e#\&.\&.e#\&.\&.e#\&.\&.e#
+                                                  
+Network address: 192\&.168\&.1\&.15:548 (TCP/IP address and port)
+Network address: 65280\&.128 (ddp address)
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBasip\-status\&.pl\fR myserver:10548
+AFP reply from myserver:10548
+Flags: 1  Cmd: 3  ID: 57005
+Reply: DSIGetStatus
+Request ID: 57005
+Machine type: Netatalk3\&.0
+AFP versions: AFP2\&.2,AFPX03,AFP3\&.1,AFP3\&.2,AFP3\&.3
+UAMs: DHX2,DHCAST128
+Volume Icon & Mask: Yes
+Flags: 
+    SupportsCopyFile
+    SupportsServerMessages
+    SupportsServerSignature
+    SupportsTCP/IP
+    SupportsSrvrNotifications
+    SupportsOpenDirectory
+    SupportsUTF8Servername
+    SupportsUUIDs
+    SupportsExtSleep
+    SupportsSuperClient
+Server name: myserver
+Signature:
+8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  \&.\&.\&.:\&.\&.\&.>o1\&.\&.\&.\&.p\&.
+                                                  
+Network address: 192\&.168\&.1\&.154:10548 (TCP/IP address and port)
+UTF8 Servername: myserver
+.fi
+.if n \{\
+.RE
+.\}
+.SH "REPORTING BUGS"
+.PP
+Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
diff --git a/man/man1/asip-status.pl.1.tmpl b/man/man1/asip-status.pl.1.tmpl
deleted file mode 100644 (file)
index 4de42c5..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-'\" t
-.\"     Title: asip-status.pl
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 24 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "ASIP\-STATUS\&.PL" "1" "24 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-asip-status.pl \- Queries AFP servers for their capabilities
-.SH "SYNOPSIS"
-.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
-\fBasip\-status\&.pl\fR\fB\fR [\-d] [\-i] [\-x] HOSTNAME[:PORT]
-
-.br
-.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
-\fBasip\-status\&.pl\fR\fB\fR \-v | \-version | \-\-version 
-.SH "DESCRIPTION"
-.PP
-\fBasip\-status\&.pl\fR
-is a perl script that sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and displays the results, namely "Machine type", the server\'s name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
-.PP
-When you don\'t supply :PORT, then the default AFP port, 548, will be used\&.
-.SH "OPTIONS"
-.PP
-\fB\-d\fR
-.RS 4
-Enable debug output\&.
-.RE
-.PP
-\fB\-i\fR
-.RS 4
-Show icon if it exists\&.
-.RE
-.PP
-\fB\-x\fR
-.RS 4
-Enable hex dump output\&.
-.RE
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version\&.
-.RE
-.SH "EXAMPLES"
-.PP
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBasip\-status\&.pl\fR 192\&.168\&.1\&.15
-AFP reply from 192\&.168\&.1\&.15:548
-Flags: 1  Cmd: 3  ID: 57005
-Reply: DSIGetStatus
-Request ID: 57005
-Machine type: Macintosh
-AFP versions: AFPVersion 1\&.1,AFPVersion 2\&.0,AFPVersion 2\&.1,AFP2\&.2
-UAMs: Cleartxt passwrd,Randnum exchange,2\-Way Randnum exchange
-Volume Icon & Mask: Yes
-Flags: 
-    SupportsCopyFile
-    SupportsChgPwd
-    SupportsServerMessages
-    SupportsServerSignature
-    SupportsTCP/IP
-    SupportsSuperClient
-Server name: bookchan
-Signature:
-04 1d 65 23 04 1d 65 23 04 1d 65 23 04 1d 65 23  \&.\&.e#\&.\&.e#\&.\&.e#\&.\&.e#
-                                                  
-Network address: 192\&.168\&.1\&.15:548 (TCP/IP address and port)
-Network address: 65280\&.128 (ddp address)
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBasip\-status\&.pl\fR myserver:10548
-AFP reply from myserver:10548
-Flags: 1  Cmd: 3  ID: 57005
-Reply: DSIGetStatus
-Request ID: 57005
-Machine type: Netatalk3\&.0
-AFP versions: AFP2\&.2,AFPX03,AFP3\&.1,AFP3\&.2,AFP3\&.3
-UAMs: DHX2,DHCAST128
-Volume Icon & Mask: Yes
-Flags: 
-    SupportsCopyFile
-    SupportsServerMessages
-    SupportsServerSignature
-    SupportsTCP/IP
-    SupportsSrvrNotifications
-    SupportsOpenDirectory
-    SupportsUTF8Servername
-    SupportsUUIDs
-    SupportsExtSleep
-    SupportsSuperClient
-Server name: myserver
-Signature:
-8a c6 12 3a 0e d9 95 3e 6f 31 e3 a9 17 f5 70 f6  \&.\&.\&.:\&.\&.\&.>o1\&.\&.\&.\&.p\&.
-                                                  
-Network address: 192\&.168\&.1\&.154:10548 (TCP/IP address and port)
-UTF8 Servername: myserver
-.fi
-.if n \{\
-.RE
-.\}
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
diff --git a/man/man1/dbd.1 b/man/man1/dbd.1
deleted file mode 100644 (file)
index d36189f..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-'\" t
-.\"     Title: dbd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 14 Sep 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "DBD" "1" "14 Sep 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-dbd \- CNID database maintenance
-.SH "SYNOPSIS"
-.HP \w'\fBdbd\fR\fB\fR\ 'u
-\fBdbd\fR\fB\fR [\-evx] {\-d\ [\-i]  | \-s\ [\-c|\-n]  | \-r\ [\-c|\-f|\-C]  | \-u} \fIvolumepath\fR
-.SH "DESCRIPTION"
-.PP
-\fBdbd\fR
-can dump, scan, reindex and rebuild
-\fINetatalk\fR
-dbd CNID databases\&. It must be run with appropiate permissions i\&.e\&. as root\&.
-\fBdbd\fR
-\fB\-s|\-r\fR
-can be run on active volumes, but
-\fBdbd \-rf\fR, which wipes the db before rebuilding it, checks and enforces that the chosen volume is not in use\&.
-.SH "COMMANDS"
-.PP
-\-d
-.RS 4
-Dump CNID database\&. With
-\fB\-i \fRdump indexes too\&.
-.RE
-.PP
-\-s
-.RS 4
-Scan volume:
-.sp
-Compare CNIDs in database with volume, test if \&.AppleDouble directories exist, test if AppleDouble files exist, report orphaned AppleDouble files, report directories inside \&.AppleDouble directories, check name encoding, heck for orphaned CNIDs in database (requires
-\fB\-e\fR)\&.
-.sp
-Options:
-.PP
-\-c
-.RS 4
-Don\'t check \&.AppleDouble stuff, only check orphaned\&.
-.RE
-.PP
-\-n
-.RS 4
-Don\'t open CNID database, skip CNID checks, only traverse filesystem
-.RE
-.RE
-.PP
-\-r
-.RS 4
-Rebuild volume:
-.sp
-Sync CNIDSs from database with volume, ensure \&.AppleDouble directories exist, ensure AppleDouble files exist, delete orphaned AppleDouble files, report directories inside \&.AppleDouble directories, check name encoding by roundtripping, delete orphaned CNIDs in database (requires
-\fB\-e\fR)\&.
-.sp
-Options:
-.PP
-\-C
-.RS 4
-Converts volume from adouble:v2 to adouble:ea
-.RE
-.PP
-\-c
-.RS 4
-Don\'t create \&.AppleDouble stuff, only cleanup orphaned\&.
-.RE
-.PP
-\-f
-.RS 4
-Wipe database and rebuild from IDs stored in AppleDouble files, only available for volumes without
-\fBnocnidcache\fR
-option\&. Implies
-\fB\-e\fR\&.
-.RE
-.RE
-.PP
-\-u
-.RS 4
-Upgrade:
-.sp
-Opens the database which triggers any necessary upgrades, then closes and exits\&.
-.RE
-.SH "OPTIONS"
-.PP
-\-e
-.RS 4
-Only work on inactive volumes and lock them (exclusive)
-.RE
-.PP
-\-x
-.RS 4
-Rebuild indexes (just for completeness, mostly useless!)
-.RE
-.PP
-\-v
-.RS 4
-verbose
-.RE
-.SH "WARNING"
-.PP
-In order to be able to run
-\fB\-rf\fR
-reconstructing the CNIDs in the database from the
-\fIAppleDouble\fR
-files, make sure you\'ve run a
-\fB\-r\fR
-rebuild sometimes before, where the CNIDs then would have been synched between database and
-\fIAppleDouble\fR
-files\&.
-.PP
-Also be careful about the option
-\fBnocnidcache\fR\&. Avoid this option if at all possible, because if prevents you from being able to use
-\fB\-f\fR\&.
-.SH "CNID BACKGROUND"
-.PP
-The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db will not know and not reflect that change\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. E\&.g\&. if several names in the path to the file or directory have changed, things may go wrong\&.
-.PP
-If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\'t changed\&.
-.SH "SEE ALSO"
-.PP
-\fBcnid_metad\fR(8),
-\fBcnid_dbd\fR(8)
diff --git a/man/man1/dbd.1.in b/man/man1/dbd.1.in
new file mode 100644 (file)
index 0000000..ad233a9
--- /dev/null
@@ -0,0 +1,81 @@
+'\" t
+.\"     Title: dbd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 28 Dec 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "DBD" "1" "28 Dec 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+dbd \- CNID database maintenance
+.SH "SYNOPSIS"
+.HP \w'\fBdbd\fR\fB\fR\ 'u
+\fBdbd\fR\fB\fR [\-fsv] \fIvolumepath\fR
+.SH "DESCRIPTION"
+.PP
+\fBdbd\fR
+scans all file and directories of AFP volumes, updating the CNID database of the volume\&. It must be run with appropriate permissions i\&.e\&. as root\&.\&.
+.SH "OPTIONS"
+.PP
+\-c
+.RS 4
+convert from adouble:v2 to adouble:ea
+.RE
+.PP
+\-f
+.RS 4
+delete and recreate CNID database
+.RE
+.PP
+\-F
+.RS 4
+location of the afp\&.conf config file
+.RE
+.PP
+\-s
+.RS 4
+scan volume: treat the volume as read only and don\*(Aqt perform any filesystem modifications
+.RE
+.PP
+\-t
+.RS 4
+show statistics while running
+.RE
+.PP
+\-v
+.RS 4
+verbose
+.RE
+.PP
+\-V
+.RS 4
+display version info
+.RE
+.SH "CNID BACKGROUND"
+.PP
+The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID database will not reflect that change\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&.
+.SH "SEE ALSO"
+.PP
+\fBcnid_metad\fR(8),
+\fBcnid_dbd\fR(8)
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
diff --git a/man/man1/macusers.1 b/man/man1/macusers.1
deleted file mode 100644 (file)
index ea823a0..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-'\" t
-.\"     Title: macusers
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 13 Oct 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "MACUSERS" "1" "13 Oct 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-macusers \- List the users connecting via AFP
-.SH "SYNOPSIS"
-.HP \w'\fBmacusers\fR\fB\fR\ 'u
-\fBmacusers\fR\fB\fR
-.HP \w'\fBmacusers\fR\fB\fR\ 'u
-\fBmacusers\fR\fB\fR \-v | \-version | \-\-version | \-h | \-help | \-\-help 
-.SH "DESCRIPTION"
-.PP
-\fBmacusers\fR
-list the users connecting via AFP\&.
-.SH "OPTIONS"
-.PP
-\fB\-v, \-version, \-\-version\fR
-.RS 4
-Show version and exit
-.RE
-.PP
-\fB\-h, \-help, \-\-help\fR
-.RS 4
-Display the help and exit
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8)
diff --git a/man/man1/macusers.1.in b/man/man1/macusers.1.in
new file mode 100644 (file)
index 0000000..ed948b2
--- /dev/null
@@ -0,0 +1,54 @@
+'\" t
+.\"     Title: macusers
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 13 Oct 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "MACUSERS" "1" "13 Oct 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+macusers \- List the users connecting via AFP
+.SH "SYNOPSIS"
+.HP \w'\fBmacusers\fR\fB\fR\ 'u
+\fBmacusers\fR\fB\fR
+.HP \w'\fBmacusers\fR\fB\fR\ 'u
+\fBmacusers\fR\fB\fR \-v | \-version | \-\-version | \-h | \-help | \-\-help 
+.SH "DESCRIPTION"
+.PP
+\fBmacusers\fR
+list the users connecting via AFP\&.
+.SH "OPTIONS"
+.PP
+\fB\-v, \-version, \-\-version\fR
+.RS 4
+Show version and exit
+.RE
+.PP
+\fB\-h, \-help, \-\-help\fR
+.RS 4
+Display the help and exit
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8)
diff --git a/man/man1/megatron.1 b/man/man1/megatron.1
deleted file mode 100644 (file)
index dfd37d0..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-'\" t
-.\"     Title: megatron
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Sep 2011
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "MEGATRON" "1" "02 Sep 2011" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
-.SH "SYNOPSIS"
-.HP \w'\fBmegatron\fR\fB\fR\fBunbin\fR\fB\fR\fBunhex\fR\fB\fR\fBunsingle\fR\fB\fR\fBhqx2bin\fR\fB\fR\fBsingle2bin\fR\fB\fR\fBmacbinary\fR\fB\fR\ 'u
-\fBmegatron\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunbin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunhex\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBunsingle\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBhqx2bin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBsingle2bin\fR\fB\fR [\fIsourcefile\fR...]
-.br
-\fBmacbinary\fR\fB\fR [\fIsourcefile\fR...]
-.SH "DESCRIPTION"
-.PP
-\fBmegatron\fR
-is used to transform files from BinHex, MacBinary, AppleSingle, or
-\fBnetatalk\fR
-style AppleDouble formats into MacBinary or
-\fBnetatalk\fR
-style AppleDouble formats\&. The
-\fBnetatalk\fR
-style AppleDouble format is the file format used by
-\fBafpd,\fR
-the
-\fBnetatalk\fR
-Apple Filing Protocol (AppleShare) server\&. BinHex, MacBinary, and AppleSingle are commonly used formats for transferring Macintosh files between machines via email or file transfer protocols\&.
-\fBmegatron\fR
-uses its name to determine what type of tranformation is being asked of it\&.
-.PP
-If
-\fBmegatron\fR
-is called as
-\fBunhex\fR
-,
-\fBunbin\fR
-or
-\fBunsingle\fR, it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble format\&. BinHex is the format most often used to send Macintosh files by e\-mail\&. Usually these files have an extension of "\&.hqx"\&. MacBinary is the format most often used by terminal emulators "on the fly" when transferring Macintosh files in binary mode\&. MacBinary files often have an extension of "\&.bin"\&. Some Macintosh LAN\-based email packages use uuencoded AppleSingle format to "attach" or "enclose" files in email\&. AppleSingle files don\'t have a standard filename extension\&.
-.PP
-If
-\fBmegatron\fR
-is called as
-\fBhqx2bin\fR,
-\fBsingle2bin\fR, or
-\fBmacbinary\fR, it will try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into MacBinary\&. This last translation may be useful in moving Macintosh files from your
-\fBafpd\fR
-server to some other machine when you can\'t copy them from the server using a Macintosh for some reason\&.
-.PP
-If
-\fBmegatron\fR
-is called with any other name, it uses the default translation, namely
-\fBunhex\fR\&.
-.PP
-If no source file is given, or if
-\fIsourcefile\fR
-is `\fB\-\fR\', and if the conversion is from a BinHex or MacBinary file,
-\fBmegatron\fR
-will read from standard input\&.
-.PP
-The filename used to store any output file is the filename that is encoded in the source file\&. MacBinary files are created with a "\&.bin" extension\&. In the case of conflicts, the old file is overwritten!
-.SH "OPTIONS"
-.PP
-\fB\-v, \-\-version\fR
-.RS 4
-Show version\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8)
diff --git a/man/man1/megatron.1.in b/man/man1/megatron.1.in
new file mode 100644 (file)
index 0000000..9b47c22
--- /dev/null
@@ -0,0 +1,102 @@
+'\" t
+.\"     Title: megatron
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 02 Sep 2011
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "MEGATRON" "1" "02 Sep 2011" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
+.SH "SYNOPSIS"
+.HP \w'\fBmegatron\fR\fB\fR\fBunbin\fR\fB\fR\fBunhex\fR\fB\fR\fBunsingle\fR\fB\fR\fBhqx2bin\fR\fB\fR\fBsingle2bin\fR\fB\fR\fBmacbinary\fR\fB\fR\ 'u
+\fBmegatron\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunbin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunhex\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBunsingle\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBhqx2bin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBsingle2bin\fR\fB\fR [\fIsourcefile\fR...]
+.br
+\fBmacbinary\fR\fB\fR [\fIsourcefile\fR...]
+.SH "DESCRIPTION"
+.PP
+\fBmegatron\fR
+is used to transform files from BinHex, MacBinary, AppleSingle, or
+\fBnetatalk\fR
+style AppleDouble formats into MacBinary or
+\fBnetatalk\fR
+style AppleDouble formats\&. The
+\fBnetatalk\fR
+style AppleDouble format is the file format used by
+\fBafpd,\fR
+the
+\fBnetatalk\fR
+Apple Filing Protocol (AppleShare) server\&. BinHex, MacBinary, and AppleSingle are commonly used formats for transferring Macintosh files between machines via email or file transfer protocols\&.
+\fBmegatron\fR
+uses its name to determine what type of transformation is being asked of it\&.
+.PP
+If
+\fBmegatron\fR
+is called as
+\fBunhex\fR
+,
+\fBunbin\fR
+or
+\fBunsingle\fR, it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into AppleDouble format\&. BinHex is the format most often used to send Macintosh files by e\-mail\&. Usually these files have an extension of "\&.hqx"\&. MacBinary is the format most often used by terminal emulators "on the fly" when transferring Macintosh files in binary mode\&. MacBinary files often have an extension of "\&.bin"\&. Some Macintosh LAN\-based email packages use uuencoded AppleSingle format to "attach" or "enclose" files in email\&. AppleSingle files don\*(Aqt have a standard filename extension\&.
+.PP
+If
+\fBmegatron\fR
+is called as
+\fBhqx2bin\fR,
+\fBsingle2bin\fR, or
+\fBmacbinary\fR, it will try to convert the file(s) from BinHex, AppleSingle, or AppleDouble into MacBinary\&. This last translation may be useful in moving Macintosh files from your
+\fBafpd\fR
+server to some other machine when you can\*(Aqt copy them from the server using a Macintosh for some reason\&.
+.PP
+If
+\fBmegatron\fR
+is called with any other name, it uses the default translation, namely
+\fBunhex\fR\&.
+.PP
+If no source file is given, or if
+\fIsourcefile\fR
+is `\fB\-\fR\*(Aq, and if the conversion is from a BinHex or MacBinary file,
+\fBmegatron\fR
+will read from standard input\&.
+.PP
+The filename used to store any output file is the filename that is encoded in the source file\&. MacBinary files are created with a "\&.bin" extension\&. In the case of conflicts, the old file is overwritten!
+.SH "OPTIONS"
+.PP
+\fB\-v, \-\-version\fR
+.RS 4
+Show version\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8)
diff --git a/man/man1/netatalk-config.1 b/man/man1/netatalk-config.1
deleted file mode 100644 (file)
index 43ebcb7..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-'\" t
-.\"     Title: netatalk-config
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 09 June 2001
-.\"    Manual: The Netatalk Project
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "NETATALK\-CONFIG" "1" "09 June 2001" "Netatalk 3.0" "The Netatalk Project"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-netatalk-config \- script to get information about the installed version of netatalk
-.SH "SYNOPSIS"
-.HP \w'\fBnetatalk\-config\fR\fB\fR\ 'u
-\fBnetatalk\-config\fR\fB\fR [\-\-prefix\ [\fI=DIR\fR]] [\-\-exec_prefix\ [\fI=DIR\fR]] [\-\-help] [\-\-version] [\-\-libs] [\-\-libs\-dirs] [\-\-libs\-names] [\-\-cflags] [\-\-macros]
-.SH "DESCRIPTION"
-.PP
-\fBnetatalk\-config\fR
-is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use the
-\fInetatalk\fR
-run\-time libraries\&.
-.SH "OPTIONS"
-.PP
-\fBnetatalk\-config\fR
-accepts the following options:
-.PP
-\fB\-\-help\fR
-.RS 4
-Print a short help for this command and exit\&.
-.RE
-.PP
-\fB\-\-version\fR
-.RS 4
-Print the currently installed version of
-\fInetatalk\fR
-on the standard output\&.
-.RE
-.PP
-\fB\-\-libs\fR
-.RS 4
-Print the linker flags that are necessary to link against the
-\fInetatalk\fR
-run\-time libraries\&.
-.RE
-.PP
-\fB\-\-libs\-dirs\fR
-.RS 4
-Print only the \-l/\-R part of \-\-libs\&.
-.RE
-.PP
-\fB\-\-libs\-names\fR
-.RS 4
-Print only the \-l part of \-\-libs\&.
-.RE
-.PP
-\fB\-\-cflags\fR
-.RS 4
-Print the compiler flags that are necessary to compile a program linked against the
-\fInetatalk\fR
-run\-time libraries\&.
-.RE
-.PP
-\fB\-\-macros\fR
-.RS 4
-Print the
-\fInetatalk\fR
-m4 directory\&.
-.RE
-.PP
-\fB\-\-prefix=PREFIX\fR
-.RS 4
-If specified, use PREFIX instead of the installation prefix that
-\fInetatalk\fR
-was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option is also used for the exec prefix if \-\-exec\-prefix was not specified\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
-.RE
-.PP
-\fB\-\-exec\e_prefix=PREFIX\fR
-.RS 4
-If specified, use PREFIX instead of the installation exec prefix that
-\fInetatalk\fR
-was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
-.RE
-.SH "COPYRIGHT"
-.PP
-Copyright \(co 1998 Owen Taylor
-.PP
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation\&.
-.PP
-Man page adapted for
-\fBnetatalk\-config\fR
-by Sebastian Rittau in 2001\&.
diff --git a/man/man1/netatalk-config.1.in b/man/man1/netatalk-config.1.in
new file mode 100644 (file)
index 0000000..f4947fa
--- /dev/null
@@ -0,0 +1,110 @@
+'\" t
+.\"     Title: netatalk-config
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 09 June 2001
+.\"    Manual: The Netatalk Project
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "NETATALK\-CONFIG" "1" "09 June 2001" "@NETATALK_VERSION@" "The Netatalk Project"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+netatalk-config \- script to get information about the installed version of netatalk
+.SH "SYNOPSIS"
+.HP \w'\fBnetatalk\-config\fR\fB\fR\ 'u
+\fBnetatalk\-config\fR\fB\fR [\-\-prefix\ [\fI=DIR\fR]] [\-\-exec_prefix\ [\fI=DIR\fR]] [\-\-help] [\-\-version] [\-\-libs] [\-\-libs\-dirs] [\-\-libs\-names] [\-\-cflags] [\-\-macros]
+.SH "DESCRIPTION"
+.PP
+\fBnetatalk\-config\fR
+is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use the
+\fInetatalk\fR
+run\-time libraries\&.
+.SH "OPTIONS"
+.PP
+\fBnetatalk\-config\fR
+accepts the following options:
+.PP
+\fB\-\-help\fR
+.RS 4
+Print a short help for this command and exit\&.
+.RE
+.PP
+\fB\-\-version\fR
+.RS 4
+Print the currently installed version of
+\fInetatalk\fR
+on the standard output\&.
+.RE
+.PP
+\fB\-\-libs\fR
+.RS 4
+Print the linker flags that are necessary to link against the
+\fInetatalk\fR
+run\-time libraries\&.
+.RE
+.PP
+\fB\-\-libs\-dirs\fR
+.RS 4
+Print only the \-l/\-R part of \-\-libs\&.
+.RE
+.PP
+\fB\-\-libs\-names\fR
+.RS 4
+Print only the \-l part of \-\-libs\&.
+.RE
+.PP
+\fB\-\-cflags\fR
+.RS 4
+Print the compiler flags that are necessary to compile a program linked against the
+\fInetatalk\fR
+run\-time libraries\&.
+.RE
+.PP
+\fB\-\-macros\fR
+.RS 4
+Print the
+\fInetatalk\fR
+m4 directory\&.
+.RE
+.PP
+\fB\-\-prefix=PREFIX\fR
+.RS 4
+If specified, use PREFIX instead of the installation prefix that
+\fInetatalk\fR
+was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option is also used for the exec prefix if \-\-exec\-prefix was not specified\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
+.RE
+.PP
+\fB\-\-exec\e_prefix=PREFIX\fR
+.RS 4
+If specified, use PREFIX instead of the installation exec prefix that
+\fInetatalk\fR
+was built with when computing the output for the \-\-cflags and \-\-libs options\&. This option must be specified before any \-\-libs or \-\-cflags options\&.
+.RE
+.SH "COPYRIGHT"
+.PP
+Copyright \(co 1998 Owen Taylor
+.PP
+Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation\&.
+.PP
+Man page adapted for
+\fBnetatalk\-config\fR
+by Sebastian Rittau in 2001\&.
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
diff --git a/man/man1/uniconv.1.in b/man/man1/uniconv.1.in
new file mode 100644 (file)
index 0000000..1ec2779
--- /dev/null
@@ -0,0 +1,203 @@
+'\" t
+.\"     Title: uniconv
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "UNICONV" "1" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+uniconv \- convert Netatalk volume encoding
+.SH "SYNOPSIS"
+.HP \w'\fBuniconv\fR\fB\fR\ 'u
+\fBuniconv\fR\fB\fR [\-ndv] \-c\ \fIcnidbackend\fR \-f\ \fIfromcode\fR \-t\ \fItocode\fR [\-m\ \fImaccode\fR] \fIvolumepath\fR
+.SH "DESCRIPTION"
+.PP
+\fBuniconv\fR
+converts the volume encoding of
+\fIvolumepath\fR
+from the
+\fIfromcode\fR
+to the
+\fItocode\fR
+encoding\&.
+.SH "OPTIONS"
+.PP
+\-c
+.RS 4
+CNID backend used on this volume, usually cdb or dbd\&. Should match the backend selected with afpd for this volume\&. If not specified, the default CNID backend "@DEFAULT_CNID_SCHEME@" is used
+.RE
+.PP
+\-d
+.RS 4
+don\*(Aqt HEX encode leading dots (:2e), equivalent to
+\fBuse dots = yes\fR
+in
+\fBafp.conf\fR(5)
+.RE
+.PP
+\-f
+.RS 4
+encoding to convert from, use ASCII for HEX encoded volumes
+.RE
+.PP
+\-h
+.RS 4
+display help
+.RE
+.PP
+\-m
+.RS 4
+Macintosh client codepage, required for HEX encoded volumes\&. Defaults to "MAC_ROMAN"
+.RE
+.PP
+\-n
+.RS 4
+"dry run", don\*(Aqt do any real changes
+.RE
+.PP
+\-t
+.RS 4
+volume encoding to convert to, e\&.g\&. UTF8
+.RE
+.PP
+\-v
+.RS 4
+verbose output, use twice for maximum logging\&.
+.RE
+.PP
+\-V
+.RS 4
+print version and exit
+.RE
+.PP
+.SH "WARNING"
+.PP
+Setting the wrong options might render your data unusable!!! Make sure you know what you are doing\&. Always backup your data first\&.
+.PP
+It is
+\fB*strongly*\fR
+recommended to do a "dry run" first and to check the output for conversion errors\&.
+.PP
+\fBafpd\fR(8)
+should
+\fInot\fR
+be running while you change the volume encoding\&. Remember to change
+\fBunix charset\fR
+or
+\fBvol charset\fR
+in
+\fBafp.conf\fR(5)
+to the new codepage, before restarting afpd\&.
+.PP
+In case of
+\fBMacChineseTraditional\fR,
+\fBMacJapanese\fR
+or
+\fBMacKorean\fR, uniconv cannot be used\&.
+.PP
+\fBUSE AT YOUR OWN RISK!!!\fR
+.SH "SELECTABLE CHARSETS"
+.PP
+Netatalk provides internal support for UTF\-8 (pre\- and decomposed) and HEX\&. If you want to use other charsets, they must be provided by
+\fBiconv\fR(1)
+.PP
+\fBuniconv\fR
+also knows iso\-8859\&.adapted, an old style 1\&.x NLS widely used\&. This is only intended for upgrading old volumes,
+\fBafpd\fR(8)
+cannot handle iso\-8859\&.adapted anymore\&.
+.SH "CNID BACKGROUND"
+.PP
+The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db, i\&.e\&. the DIDNAME index, gets inconsistent\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. I\&.e\&. if several names in the path to the file or directory have changed, things may go wrong\&.
+.PP
+If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\*(Aqt changed\&.
+\fBuniconv\fR
+therefore updates the CNID entry for each file/directory directly after it changes the name to avoid inconsistencies\&. The two supported backends for volumes, dbd and cdb, use the same CNID db format\&. Therefore, you
+\fIcould\fR
+use
+\fBuniconv\fR
+with cdb and
+\fBafpd\fR
+with dbd later\&.
+.PP
+\fBWarning\fR: There must not be two processes opening the CNID database using different backends at once! If a volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
+\fBuniconv\fR
+with cdb, the result will be a corrupted CNID database, as the two backends use different locking schemes\&. You might run into additional problems, e\&.g\&. if dbd is compiled with transactions, cdb will not update the transaction logs\&.
+.PP
+In general, it is recommended to use the same backend for
+\fBuniconv\fR
+you are using with
+\fBafpd\fR(8)\&.
+.SH "EXAMPLES"
+.PP
+convert 1\&.x CAP encoded volume to UTF\-8, clients used MacRoman codepage, cnidscheme is dbd:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-c dbd \-f ASCII \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert iso8859\-1 volume to UTF\-8, cnidscheme is cdb:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-c cdb \-f ISO\-8859\-1 \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert 1\&.x volume using iso8859\-1 adapted NLS to HEX encoding:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-f ISO\-8859\-ADAPTED \-t ASCII \-m MAC_ROMAN/path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+convert UTF\-8 volume to HEX, for MacCyrillic clients:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+example%\fB uniconv \-f UTF8 \-t ASCII \-m MAC_CYRILLIC /path/to/share\fR
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5),\fBafpd\fR(8),\fBiconv\fR(1),\fBcnid_metad\fR(8),\fBcnid_dbd\fR(8)
diff --git a/man/man1/uniconv.1.tmpl b/man/man1/uniconv.1.tmpl
deleted file mode 100644 (file)
index 729d35e..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-'\" t
-.\"     Title: uniconv
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "UNICONV" "1" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-uniconv \- convert Netatalk volume encoding
-.SH "SYNOPSIS"
-.HP \w'\fBuniconv\fR\fB\fR\ 'u
-\fBuniconv\fR\fB\fR [\-ndv] \-c\ \fIcnidbackend\fR \-f\ \fIfromcode\fR \-t\ \fItocode\fR [\-m\ \fImaccode\fR] \fIvolumepath\fR
-.SH "DESCRIPTION"
-.PP
-\fBuniconv\fR
-converts the volume encoding of
-\fIvolumepath\fR
-from the
-\fIfromcode\fR
-to the
-\fItocode\fR
-encoding\&.
-.SH "OPTIONS"
-.PP
-\-c
-.RS 4
-CNID backend used on this volume, usually cdb or dbd\&. Should match the backend selected with afpd for this volume\&. If not specified, the default CNID backend ":DEFAULT_CNID_SCHEME:" is used
-.RE
-.PP
-\-d
-.RS 4
-don\'t HEX encode leading dots (:2e), equivalent to
-\fBuse dots = yes\fR
-in
-\fBafp.conf\fR(5)
-.RE
-.PP
-\-f
-.RS 4
-encoding to convert from, use ASCII for HEX encoded volumes
-.RE
-.PP
-\-h
-.RS 4
-display help
-.RE
-.PP
-\-m
-.RS 4
-Macintosh client codepage, required for HEX encoded volumes\&. Defaults to "MAC_ROMAN"
-.RE
-.PP
-\-n
-.RS 4
-"dry run", don\'t do any real changes
-.RE
-.PP
-\-t
-.RS 4
-volume encoding to convert to, e\&.g\&. UTF8
-.RE
-.PP
-\-v
-.RS 4
-verbose output, use twice for maximum logging\&.
-.RE
-.PP
-\-V
-.RS 4
-print version and exit
-.RE
-.PP
-.SH "WARNING"
-.PP
-Setting the wrong options might render your data unusable!!! Make sure you know what you are doing\&. Always backup your data first\&.
-.PP
-It is
-\fB*strongly*\fR
-recommended to do a "dry run" first and to check the output for conversion errors\&.
-.PP
-\fBafpd\fR(8)
-should
-\fInot\fR
-be running while you change the volume encoding\&. Remember to change
-\fBvolcodepage\fR
-in
-\fBAppleVolumes.default\fR(5)
-to the new codepage, before restarting afpd\&.
-.PP
-In case of
-\fBMacChineseTraditional\fR,
-\fBMacJapanese\fR
-or
-\fBMacKorean\fR, uniconv cannot be used\&.
-.PP
-\fBUSE AT YOUR OWN RISK!!!\fR
-.SH "SELECTABLE CHARSETS"
-.PP
-Netatalk provides internal support for UTF\-8 (pre\- and decomposed) and HEX\&. If you want to use other charsets, they must be provided by
-\fBiconv\fR(1)
-.PP
-\fBuniconv\fR
-also knows iso\-8859\&.adapted, an old style 1\&.x NLS widely used\&. This is only intended for upgrading old volumes,
-\fBafpd\fR(8)
-cannot handle iso\-8859\&.adapted anymore\&.
-.SH "CNID BACKGROUND"
-.PP
-The CNID backends maintains name to ID mappings\&. If you change a filename outside afpd(8) (shell, samba), the CNID db, i\&.e\&. the DIDNAME index, gets inconsistent\&. Netatalk tries to recover from such inconsistencies as gracefully as possible\&. The mechanisms to resolve such inconsistencies may fail sometimes, though, as this is not an easy task to accomplish\&. I\&.e\&. if several names in the path to the file or directory have changed, things may go wrong\&.
-.PP
-If you change a lot of filenames at once, chances are higher that the afpds fallback mechanisms fail, i\&.e\&. files will be assigned new IDs, even though the file hasn\'t changed\&.
-\fBuniconv\fR
-therefore updates the CNID entry for each file/directory directly after it changes the name to avoid inconsistencies\&. The two supported backends for volumes, dbd and cdb, use the same CNID db format\&. Therefore, you
-\fIcould\fR
-use
-\fBuniconv\fR
-with cdb and
-\fBafpd\fR
-with dbd later\&.
-.PP
-\fBWarning\fR: There must not be two processes opening the CNID database using different backends at once! If a volume is still opened with dbd (cnid_metad/cnid_dbd) and you start
-\fBuniconv\fR
-with cdb, the result will be a corrupted CNID database, as the two backends use different locking schemes\&. You might run into additional problems, e\&.g\&. if dbd is compiled with transactions, cdb will not update the transaction logs\&.
-.PP
-In general, it is recommended to use the same backend for
-\fBuniconv\fR
-you are using with
-\fBafpd\fR(8)\&.
-.SH "EXAMPLES"
-.PP
-convert 1\&.x CAP encoded volume to UTF\-8, clients used MacRoman codepage, cnidscheme is dbd:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-c dbd \-f ASCII \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert iso8859\-1 volume to UTF\-8, cnidscheme is cdb:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-c cdb \-f ISO\-8859\-1 \-t UTF8 \-m MAC_ROMAN /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert 1\&.x volume using iso8859\-1 adapted NLS to HEX encoding:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-f ISO\-8859\-ADAPTED \-t ASCII \-m MAC_ROMAN/path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-convert UTF\-8 volume to HEX, for MacCyrillic clients:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-example%\fB uniconv \-f UTF8 \-t ASCII \-m MAC_CYRILLIC /path/to/share\fR
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafp.conf\fR(5),\fBafpd\fR(8),\fBiconv\fR(1),\fBcnid_metad\fR(8),\fBcnid_dbd\fR(8)
index 40dd5c2a888b56b5d6d1fa45a6a2974998fd01ae..42ffb541355827f3b669c55c8834bd4ff96a677b 100644 (file)
@@ -1 +1 @@
-.so man1/megatron.1
+.so megatron.1
index ba10eeb069f64c047345d71684667225ae54657d..999046039f04554512659561961af530f7e05c69 100644 (file)
@@ -1,4 +1,3 @@
 Makefile
 Makefile.in
 *.5
-*.o
index fd2edf6ca7aa9b84d68dbd9844fdbb39687827fe..0453a03c3311f50aefdbbfdc339dd72d122f4fa8 100644 (file)
@@ -1,25 +1,9 @@
 # Makefile.am for man/man5/
 
-pkgconfdir = @PKGCONFDIR@
+man_MANS = \
+       afp.conf.5 \
+       afp_signature.conf.5 \
+       afp_voluuid.conf.5 \
+       extmap.conf.5
 
-SUFFIXES = .tmpl .
-
-.tmpl:
-       sed -e "s@:SBINDIR:@${sbindir}@g" \
-           -e "s@:BINDIR:@${bindir}@g" \
-           -e "s@:ETCDIR:@${pkgconfdir}@g" \
-           -e "s@:LIBDIR:@${libdir}@g" \
-           -e "s@:STATEDIR:@${localstatedir}@g" \
-           -e "s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@g" \
-           -e "s@:COMPILED_BACKENDS:@${compiled_backends}@g" \
-           <$< >$@
-
-GENERATED_MANS = afp.conf.5 afp_signature.conf.5 afp_voluuid.conf.5
-TEMPLATE_FILES = afp.conf.5.tmpl afp_signature.conf.5.tmpl afp_voluuid.conf.5.tmpl
-NONGENERATED_MANS =
-
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
-
-CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in
new file mode 100644 (file)
index 0000000..90ac57a
--- /dev/null
@@ -0,0 +1,1187 @@
+'\" t
+.\"     Title: afp.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 30 Apr 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP\&.CONF" "5" "30 Apr 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp.conf \- Netatalk configuration file
+.SH "SYNOPSIS"
+.PP
+The
+afp\&.conf
+file is the configuration file for the
+\fBNetatalk\fR
+AFP file server\&.
+.PP
+All AFP specific configuration and AFP volume definitions are done via this file\&.
+.SH "FILE FORMAT"
+.PP
+The file consists of sections and parameters\&. A section begins with the name of the section in square brackets and continues until the next section begins\&. Sections contain parameters of the form:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+    \fIname\fR = \fIvalue \fR
+    
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+The file is line\-based \- that is, each newline\-terminated line represents either a comment, a section name or a parameter\&.
+.PP
+Section and parameter names are case sensitive\&.
+.PP
+Only the first equals sign in a parameter is significant\&. Whitespace before or after the first equals sign is discarded\&. Leading, trailing and internal whitespace in section and parameter names is irrelevant\&. Leading and trailing whitespace in a parameter value is discarded\&. Internal whitespace within a parameter value is retained verbatim\&.
+.PP
+Any line beginning with a semicolon (\(lq;\(rq) or a hash (\(lq#\(rq) character is ignored, as are lines containing only whitespace\&.
+.PP
+Any line ending in a
+\(lq \e \(rq
+is continued on the next line in the customary UNIX fashion\&.
+.PP
+The values following the equals sign in parameters are all either a string (no quotes needed) or a boolean, which may be given as yes/no, 1/0 or true/false\&. Case is not significant in boolean values, but is preserved in string values\&. Some items such as create masks are numeric\&.
+.PP
+The parameter
+\fBinclude = \fR\fB\fIpath\fR\fR
+allows you to include one config file inside another\&. The file is included literally, as though typed in place\&. Nested includes are not supported\&.
+.SH "SECTION DESCRIPTIONS"
+.PP
+Each section in the configuration file (except for the [Global] section) describes a shared resource (known as a
+\(lqvolume\(rq)\&. The section name is the name of the volume and the parameters within the section define the volume attributes and options\&.
+.PP
+There are two special sections, [Global] and [Homes], which are described under
+\fIspecial sections\fR\&. The following notes apply to ordinary section descriptions\&.
+.PP
+A volume consists of a directory to which access is being given plus a description of the access rights which are granted to the user of the service\&. For volumes the
+\fBpath\fR
+option must specify the directory to share\&.
+.PP
+Any volume section without
+\fBpath\fR
+option is considered a
+\fIvol preset\fR
+which can be selected in other volume sections via the
+\fBvol preset\fR
+option and constitutes defaults for the volume\&. For any option specified both in a preset
+\fIand\fR
+in a volume section the volume section setting completely substitutes the preset option\&.
+.PP
+The access rights granted by the server are masked by the access rights granted to the specified or guest UNIX user by the host system\&. The server does not grant more access than the host system grants\&.
+.PP
+The following sample section defines an AFP volume\&. The user has full access to the path
+/foo/bar\&. The share is accessed via the share name
+baz:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [baz]
+    path = /foo/bar 
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SPECIAL SECTIONS"
+.SS "The [Global] section"
+.PP
+Parameters in this section apply to the server as a whole\&. Parameters denoted by a (G) below are must be set in this section\&.
+.SS "The [Homes] section"
+.PP
+This section enable sharing of the UNIX server user home directories\&. Specifying an optional
+\fBpath\fR
+parameter means that not the whole user home will be shared but the subdirectory
+\fBpath\fR\&. It is necessary to define the
+\fBbasedir regex\fR
+option\&. It should be a regex which matches the parent directory of the user homes\&. Parameters denoted by a (H) belong to volume sections\&. The optional parameter
+\fBhome name\fR
+can be used to change the AFP volume name which
+\fI$u\*(Aqs home\fR
+by default\&. See below under VARIABLE SUBSTITUTIONS\&.
+.PP
+The following example illustrates this\&. Given all user home directories are stored under
+/home:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [Homes]
+      path = afp\-data
+      basedir regex = /home
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+For a user
+\fIjohn\fR
+this results in an AFP home volume with a path of
+/home/john/afp\-data\&.
+.PP
+If
+\fBbasedir regex\fR
+contains symlink, set the canonicalized absolute path\&. When
+/home
+links to
+/usr/home:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ [Homes]
+      basedir regex = /usr/home
+.fi
+.if n \{\
+.RE
+.\}
+.SH "PARAMETERS"
+.PP
+Parameters define the specific attributes of sections\&.
+.PP
+Some parameters are specific to the [Global] section (e\&.g\&.,
+\fIlog type\fR)\&. All others are permissible only in volume sections\&. The letter
+\fIG\fR
+in parentheses indicates that a parameter is specific to the [Global] section\&. The letter
+\fIV\fR
+indicates that a parameter can be specified in a volume specific section\&.
+.SH "VARIABLE SUBSTITUTIONS"
+.PP
+You can use variables in volume names\&. The use of variables in paths is not supported for now\&.
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 1.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  1." 4.2
+.\}
+if you specify an unknown variable, it will not get converted\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 2.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  2." 4.2
+.\}
+if you specify a known variable, but that variable doesn\*(Aqt have a value, it will get ignored\&.
+.RE
+.PP
+The variables which can be used for substitutions are:
+.PP
+$b
+.RS 4
+basename
+.RE
+.PP
+$c
+.RS 4
+client\*(Aqs ip address
+.RE
+.PP
+$d
+.RS 4
+volume pathname on server
+.RE
+.PP
+$f
+.RS 4
+full name (contents of the gecos field in the passwd file)
+.RE
+.PP
+$g
+.RS 4
+group name
+.RE
+.PP
+$h
+.RS 4
+hostname
+.RE
+.PP
+$i
+.RS 4
+client\*(Aqs ip, without port
+.RE
+.PP
+$s
+.RS 4
+server name (this can be the hostname)
+.RE
+.PP
+$u
+.RS 4
+user name (if guest, it is the user that guest is running as)
+.RE
+.PP
+$v
+.RS 4
+volume name
+.RE
+.PP
+$$
+.RS 4
+prints dollar sign ($)
+.RE
+.SH "EXPLANATION OF GLOBAL PARAMETERS"
+.SS "Authentication Options"
+.PP
+ad domain = \fIDOMAIN\fR \fB(G)\fR
+.RS 4
+Append @DOMAIN to username when authenticating\&. Useful in Active Directory environments that otherwise would require the user to enter the full user@domain string\&.
+.RE
+.PP
+admin auth user = \fIuser\fR \fB(G)\fR
+.RS 4
+Specifying eg "\fBadmin auth user = root\fR" whenever a normal user login fails, afpd will try to authenticate as the specified
+\fBadmin auth user\fR\&. If this succeeds, a normal session is created for the original connecting user\&. Said differently: if you know the password of
+\fBadmin auth user\fR, you can authenticate as any other user\&.
+.RE
+.PP
+k5 keytab = \fIpath\fR \fB(G)\fR, k5 service = \fIservice\fR \fB(G)\fR, k5 realm = \fIrealm\fR \fB(G)\fR
+.RS 4
+These are required if the server supports the Kerberos 5 authentication UAM\&.
+.RE
+.PP
+nt domain = \fIDOMAIN\fR \fB(G)\fR, nt separator = \fISEPARATOR\fR \fB(G)\fR
+.RS 4
+Use for eg\&. winbind authentication, prepends both strings before the username from login and then tries to authenticate with the result through the available and active UAM authentication modules\&.
+.RE
+.PP
+save password = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Enables or disables the ability of clients to save passwords locally\&.
+.RE
+.PP
+set password = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Enables or disables the ability of clients to change their passwords via chooser or the "connect to server" dialog\&.
+.RE
+.PP
+uam list = \fIuam list\fR \fB(G)\fR
+.RS 4
+Space or comma separated list of UAMs\&. (The default is "uams_dhx\&.so uams_dhx2\&.so")\&.
+.sp
+The most commonly used UAMs are:
+.PP
+uams_guest\&.so
+.RS 4
+allows guest logins
+.RE
+.PP
+uams_clrtxt\&.so
+.RS 4
+(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&. (legacy)
+.RE
+.PP
+uams_randum\&.so
+.RS 4
+allows Random Number and Two\-Way Random Number Exchange for authentication (requires a separate file containing the passwords, either @pkgconfdir@/afppasswd file or the one specified via "\fBpasswd file\fR"\&. See
+\fBafppasswd\fR(1)
+for details\&. (legacy)
+.RE
+.PP
+uams_dhx\&.so
+.RS 4
+(uams_dhx_pam\&.so or uams_dhx_passwd\&.so) Allow Diffie\-Hellman eXchange (DHX) for authentication\&.
+.RE
+.PP
+uams_dhx2\&.so
+.RS 4
+(uams_dhx2_pam\&.so or uams_dhx2_passwd\&.so) Allow Diffie\-Hellman eXchange 2 (DHX2) for authentication\&.
+.RE
+.PP
+uam_gss\&.so
+.RS 4
+Allow Kerberos V for authentication (optional)
+.RE
+.RE
+.PP
+uam path = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the default path for UAMs for this server (default is @libdir@/netatalk)\&.
+.RE
+.SS "Charset Options"
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the big changes was, that AFP3 uses Unicode names encoded as Decomposed UTF\-8 (UTF8\-MAC)\&. Previous AFP/OS versions used charsets like MacRoman, MacCentralEurope, etc\&.
+.PP
+To be able to serve AFP3 and older clients at the same time,
+\fBafpd\fR
+needs to be able to convert between UTF\-8 and Mac charsets\&. Even OS X clients partly still rely on the mac charset\&. As there\*(Aqs no way,
+\fBafpd\fR
+can detect the codepage a pre AFP3 client uses, you have to specify it using the
+\fBmac charset\fR
+option\&. The default is MacRoman, which should be fine for most western users\&.
+.PP
+As
+\fBafpd\fR
+needs to interact with UNIX operating system as well, it need\*(Aqs to be able to convert from UTF8\-MAC / Mac charset to the UNIX charset\&. By default
+\fBafpd\fR
+uses
+\fIUTF8\fR\&. You can set the UNIX charset using the
+\fBunix charset\fR
+option\&. If you\*(Aqre using extended characters in the configuration files for
+\fBafpd\fR, make sure your terminal matches the
+\fBunix charset\fR\&.
+.PP
+mac charset = \fICHARSET\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the Mac clients charset, e\&.g\&.
+\fIMAC_ROMAN\fR\&. This is used to convert strings and filenames to the clients codepage for OS9 and Classic, i\&.e\&. for authentication and AFP messages (SIGUSR2 messaging)\&. This will also be the default for the volumes
+\fBmac charset\fR\&. Defaults to
+\fIMAC_ROMAN\fR\&.
+.RE
+.PP
+unix charset = \fICHARSET\fR \fB(G)\fR
+.RS 4
+Specifies the servers unix charset, e\&.g\&.
+\fIISO\-8859\-15\fR
+or
+\fIEUC\-JP\fR\&. This is used to convert strings to/from the systems locale, e\&.g\&. for authentication, server messages and volume names\&. If
+\fILOCALE\fR
+is set, the systems locale is used\&. Defaults to
+\fIUTF8\fR\&.
+.RE
+.PP
+vol charset = \fICHARSET\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the encoding of the volumes filesystem\&. By default, it is the same as
+\fBunix charset\fR\&.
+.RE
+.SS "Password Options"
+.PP
+passwd file = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the path to the Randnum UAM passwd file for this server (default is @pkgconfdir@/afppasswd)\&.
+.RE
+.PP
+passwd minlen = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the minimum password length, if supported by the UAM
+.RE
+.SS "Network Options"
+.PP
+advertise ssh = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Allows old Mac OS X clients (10\&.3\&.3\-10\&.4) to automagically establish a tunneled AFP connection through SSH\&. If this option is set, the server\*(Aqs answers to client\*(Aqs FPGetSrvrInfo requests contain an additional entry\&. It depends on both client\*(Aqs settings and a correctly configured and running
+\fBsshd\fR(8)
+on the server to let things work\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+Setting this option is not recommended since globally encrypting AFP connections via SSH will increase the server\*(Aqs load significantly\&. On the other hand, Apple\*(Aqs client side implementation of this feature in MacOS X versions prior to 10\&.3\&.4 contained a security flaw\&.
+.sp .5v
+.RE
+.RE
+.PP
+afp interfaces = \fIname [name \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the network interfaces that the server should listens on\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&.
+.RE
+.PP
+afp listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the IP address that the server should advertise
+\fBand\fR
+listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.RE
+.PP
+afp port = \fIport number\fR \fB(G)\fR
+.RS 4
+Allows a different TCP port to be used for AFP\&. The default is 548\&. Also sets the default port applied when none specified in an
+\fBafp listen\fR
+option\&.
+.RE
+.PP
+cnid listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specifies the IP address that the CNID server should listen on\&. The default is
+\fBlocalhost:4700\fR\&.
+.RE
+.PP
+disconnect time = \fInumber\fR \fB(G)\fR
+.RS 4
+Keep disconnected AFP sessions for
+\fInumber\fR
+hours before dropping them\&. Default is 24 hours\&.
+.RE
+.PP
+dsireadbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Scale factor that determines the size of the DSI/TCP readahead buffer, default is 12\&. This is multiplies with the DSI server quantum (default ~300k) to give the size of the buffer\&. Increasing this value might increase throughput in fast local networks for volume to volume copies\&.
+\fINote\fR: This buffer is allocated per afpd child process, so specifying large values will eat up large amount of memory (buffer size * number of clients)\&.
+.RE
+.PP
+fqdn = \fIname:port\fR \fB(G)\fR
+.RS 4
+Specifies a fully\-qualified domain name, with an optional port\&. This is discarded if the server cannot resolve it\&. This option is not honored by AppleShare clients <= 3\&.8\&.3\&. This option is disabled by default\&. Use with caution as this will involve a second name resolution step on the client side\&. Also note that afpd will advertise this name:port combination but not automatically listen to it\&.
+.RE
+.PP
+hostname = \fIname\fR \fB(G)\fR
+.RS 4
+Use this instead of the result from calling hostname for determining which IP address to advertise, therefore the hostname is resolved to an IP which is the advertised\&. This is NOT used for listening and it is also overwritten by
+\fBafp listen\fR\&.
+.RE
+.PP
+max connections = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the maximum number of clients that can simultaneously connect to the server (default is 200)\&.
+.RE
+.PP
+server quantum = \fInumber\fR \fB(G)\fR
+.RS 4
+This specifies the DSI server quantum\&. The default value is 1 MB\&. The maximum value is 0xFFFFFFFFF, the minimum is 32000\&. If you specify a value that is out of range, the default value will be set\&. Do not change this value unless you\*(Aqre absolutely sure, what you\*(Aqre doing
+.RE
+.PP
+sleep time = \fInumber\fR \fB(G)\fR
+.RS 4
+Keep sleeping AFP sessions for
+\fInumber\fR
+hours before disconnecting clients in sleep mode\&. Default is 10 hours\&.
+.RE
+.PP
+tcprcvbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Try to set TCP receive buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+tcpsndbuf = \fInumber\fR \fB(G)\fR
+.RS 4
+Try to set TCP send buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+use sendfile = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to use sendfile
+syscall for sending file data to clients\&.
+.RE
+.PP
+zeroconf = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to use automatic Zeroconf
+service registration if Avahi or mDNSResponder were compiled in\&.
+.RE
+.SS "Miscellaneous Options"
+.PP
+admin group = \fIgroup\fR \fB(G)\fR
+.RS 4
+Allows users of a certain group to be seen as the superuser when they log in\&. This option is disabled by default\&.
+.RE
+.PP
+afp read locks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to apply locks to the byte region read in FPRead calls\&. The AFP spec mandates this, but it\*(Aqs not really in line with UNIX semantics and is a performance hug\&.
+.RE
+.PP
+afpstats = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to provide AFP runtime statistics (connected users, open volumes) via dbus\&.
+.RE
+.PP
+basedir regex = \fIregex\fR \fB(H)\fR
+.RS 4
+Regular expression which matches the parent directory of the user homes\&. If
+\fBbasedir regex\fR
+contains symlink, you must set the canonicalized absolute path\&. In the simple case this is just a path ie
+\fBbasedir regex = /home\fR
+.RE
+.PP
+close vol = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+Whether to close volumes possibly opened by clients when they\*(Aqre removed from the configuration and the configuration is reloaded\&.
+.RE
+.PP
+cnid server = \fIipaddress[:port]\fR \fB(G)/(V)\fR
+.RS 4
+Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
+.RE
+.PP
+dircachesize = \fInumber\fR \fB(G)\fR
+.RS 4
+Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
+.sp
+Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
+.RE
+.PP
+extmap file = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the path to the file which defines file extension type/creator mappings\&. (default is @pkgconfdir@/extmap\&.conf)\&.
+.RE
+.PP
+guest account = \fIname\fR \fB(G)\fR
+.RS 4
+Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.
+.RE
+.PP
+home name = \fIname\fR \fB(H)\fR
+.RS 4
+AFP user home volume name\&. The default is
+\fIuser\*(Aqs home\fR\&.
+.RE
+.PP
+login message = \fImessage\fR \fB(G)/(V)\fR
+.RS 4
+Sets a message to be displayed when clients logon to the server\&. The message should be in
+\fBunix charset\fR
+and should be quoted\&. Extended characters are allowed\&.
+.RE
+.PP
+mimic model = \fImodel\fR \fB(G)\fR
+.RS 4
+Specifies the icon model that appears on clients\&. Defaults to off\&. Note that afpd must support Zeroconf\&. Examples: RackMac (same as Xserve), PowerBook, PowerMac, Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro, AppleTV1,1, AirPort\&.
+.RE
+.PP
+signature = <text> \fB(G)\fR
+.RS 4
+Specify a server signature\&. The maximum length is 16 characters\&. This option is useful for clustered environments, to provide fault isolation etc\&. By default, afpd generate signature and saving it to
+@localstatedir@/netatalk/afp_signature\&.conf
+automatically (based on random number)\&. See also asip\-status\&.pl(1)\&.
+.RE
+.PP
+solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
+.RE
+.PP
+vol dbpath = \fIpath\fR \fB(G)\fR
+.RS 4
+Sets the database information to be stored in path\&. You have to specify a writable location, even if the volume is read only\&. The default is
+@localstatedir@/netatalk/CNID/\&.
+.RE
+.PP
+volnamelen = \fInumber\fR \fB(G)\fR
+.RS 4
+Max length of UTF8\-MAC volume name for Mac OS X\&. Note that Hangul is especially sensitive to this\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ 73: limit of Mac OS X 10\&.1 80: limit of Mac
+            OS X 10\&.4/10\&.5 (default) 255: limit of recent Mac OS
+            X
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Mac OS 9 and earlier are not influenced by this, because Maccharset volume name is always limited to 27 bytes\&.
+.RE
+.PP
+vol preset = \fIname\fR \fB(G)/(V)\fR
+.RS 4
+Use section
+\fBname\fR
+as option preset for all volumes (when set in the [Global] section) or for one volume (when set in that volume\*(Aqs section)\&.
+.RE
+.SS "Logging Options"
+.PP
+log file = \fIlogfile\fR \fB(G)\fR
+.RS 4
+If not specified Netatalk logs to syslogs daemon facility\&. Otherwise it logs to
+\fBlogfile\fR\&.
+.RE
+.PP
+log level = \fItype:level [type:level \&.\&.\&.]\fR \fB(G)\fR, log level = \fItype:level,[type:level, \&.\&.\&.]\fR \fB(G)\fR
+.RS 4
+Specify that any message of a loglevel up to the given
+\fBlog level\fR
+should be logged\&.
+.sp
+By default afpd logs to syslog with a default logging setup equivalent to
+\fBdefault:note\fR
+.sp
+logtypes: default, afpdaemon, logger, uamsdaemon
+.sp
+loglevels: severe, error, warn, note, info, debug, debug6, debug7, debug8, debug9, maxdebug
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+Both logtype and loglevels are case insensitive\&.
+.sp .5v
+.RE
+.RE
+.SS "Filesystem Change Events (FCE)"
+.PP
+Netatalk includes a nifty filesystem change event mechanism where afpd processes notify interested listeners about certain filesystem event by UDP network datagrams\&.
+.PP
+fce listener = \fIhost[:port]\fR \fB(G)\fR
+.RS 4
+Enables sending FCE events to the specified
+\fIhost\fR, default
+\fIport\fR
+is 12250 if not specified\&. Specifying multiple listeners is done by having this option once for each of them\&.
+.RE
+.PP
+fce events = \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR \fB(G)\fR
+.RS 4
+Specifies which FCE events are active, default is
+\fIfmod,fdel,ddel,fcre,dcre\fR\&.
+.RE
+.PP
+fce coalesce = \fIall|delete|create\fR \fB(G)\fR
+.RS 4
+Coalesce FCE events\&.
+.RE
+.PP
+fce holdfmod = \fIseconds\fR \fB(G)\fR
+.RS 4
+This determines the time delay in seconds which is always waited if another file modification for the same file is done by a client before sending an FCE file modification event (fmod)\&. For example saving a file in Photoshop would generate multiple events by itself because the application is opening, modifying and closing a file multiple times for every "save"\&. Default: 60 seconds\&.
+.RE
+.SS "Debug Parameters"
+.PP
+These options are useful for debugging only\&.
+.PP
+tickleval = \fInumber\fR \fB(G)\fR
+.RS 4
+Sets the tickle timeout interval (in seconds)\&. Defaults to 30\&.
+.RE
+.PP
+timeout = \fInumber\fR \fB(G)\fR
+.RS 4
+Specify the number of tickles to send before timing out a connection\&. The default is 4, therefore a connection will timeout after 2 minutes\&.
+.RE
+.PP
+client polling = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
+.RS 4
+With this option enabled, afpd won\*(Aqt advertise that it is capable of server notifications, so that connected clients poll the server every 10 seconds to detect changes in opened server windows\&.
+\fINote\fR: Depending on the number of simultaneously connected clients and the network\*(Aqs speed, this can lead to a significant higher load on your network!
+.sp
+Do not use this option any longer as present Netatalk correctly supports server notifications, allowing connected clients to update folder listings in case another client changed the contents\&.
+.RE
+.SS "Options for ACL handling"
+.PP
+By default, the effective permission of the authenticated user are only mapped to the mentioned UARights permission structure, not the UNIX mode\&. You can adjust this behaviour with the configuration option
+\fBmac acls\fR:
+.PP
+map acls = \fInone|rights|mode\fR \fB(G)\fR
+.RS 4
+.PP
+none
+.RS 4
+no mapping of ACLs
+.RE
+.PP
+rights
+.RS 4
+effective permissions are mapped to UARights structure\&. This is the default\&.
+.RE
+.PP
+mode
+.RS 4
+ACLs are additionally mapped to the UNIX mode of the filesystem object\&.
+.RE
+.RE
+.PP
+If you want to be able to display ACLs on the client, you must setup both client and server as part on a authentication domain (directory service, eg LDAP, Open Directory, Active Directory)\&. The reason is, in OS X ACLs are bound to UUIDs, not just uid\*(Aqs or gid\*(Aqs\&. Therefor Netatalk must be able to map every filesystem uid and gid to a UUID so that it can return the server side ACLs which are bound to UNIX uid and gid mapped to OS X UUIDs\&.
+.PP
+Netatalk can query a directory server using LDAP queries\&. Either the directory server already provides an UUID attribute for user and groups (Active Directory, Open Directory) or you reuse an unused attribute (or add a new one) to you directory server (eg OpenLDAP)\&.
+.PP
+The following LDAP options must be configured for Netatalk:
+.PP
+ldap auth method = \fInone|simple|sasl\fR \fB(G)\fR
+.RS 4
+Authentication method:
+\fBnone | simple | sasl\fR
+.PP
+none
+.RS 4
+anonymous LDAP bind
+.RE
+.PP
+simple
+.RS 4
+simple LDAP bind
+.RE
+.PP
+sasl
+.RS 4
+SASL\&. Not yet supported !
+.RE
+.RE
+.PP
+ldap auth dn = \fIdn\fR \fB(G)\fR
+.RS 4
+Distinguished Name of the user for simple bind\&.
+.RE
+.PP
+ldap auth pw = \fIpassword\fR \fB(G)\fR
+.RS 4
+Distinguished Name of the user for simple bind\&.
+.RE
+.PP
+ldap server = \fIhost\fR \fB(G)\fR
+.RS 4
+Name or IP address of your LDAP Server\&. This is only needed for explicit ACL support in order to be able to query LDAP for UUIDs\&.
+.sp
+You can use
+\fBafpldaptest\fR(1)
+to syntactically check your config\&.
+.RE
+.PP
+ldap userbase = \fIbase dn\fR \fB(G)\fR
+.RS 4
+DN of the user container in LDAP\&.
+.RE
+.PP
+ldap userscope = \fIscope\fR \fB(G)\fR
+.RS 4
+Search scope for user search:
+\fBbase | one | sub\fR
+.RE
+.PP
+ldap groupbase = \fIbase dn\fR \fB(G)\fR
+.RS 4
+DN of the group container in LDAP\&.
+.RE
+.PP
+ldap groupscope = \fIscope\fR \fB(G)\fR
+.RS 4
+Search scope for user search:
+\fBbase | one | sub\fR
+.RE
+.PP
+ldap uuid attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the UUIDs\&.
+.sp
+Note: this is used both for users and groups\&.
+.RE
+.PP
+ldap name attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the users short name\&.
+.RE
+.PP
+ldap uuid string = \fISTRING\fR \fB(G)\fR
+.RS 4
+Format of the uuid string in the directory\&. A series of x and \-, where every x denotes a value 0\-9a\-f and every \- is a separator\&.
+.sp
+Default: xxxxxxxx\-xxxx\-xxxx\-xxxx\-xxxxxxxxxxxx
+.RE
+.PP
+ldap uuid encoding = \fIstring | ms\-guid (default: string)\fR \fB(G)\fR
+.RS 4
+Format of the UUID of the LDAP attribute, allows usage of the binary objectGUID fields from Active Directory\&. If left unspecified, string is the default, which passes through the ASCII UUID returned by most other LDAP stores\&. If set to ms\-guid, the internal UUID representation is converted to and from the binary format used in the objectGUID attribute found on objects in Active Directory when interacting with the server\&.
+.PP
+string
+.RS 4
+UUID is a string, use with eg OpenDirectory\&.
+.RE
+.PP
+ms\-guid
+.RS 4
+Binary objectGUID from Active Directory
+.RE
+.RE
+.PP
+ldap group attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the groups short name\&.
+.RE
+.SH "EXPLANATION OF VOLUME PARAMETERS"
+.SS "Parameters"
+.PP
+The section name defines the volume name\&. No two volumes may have the same name\&. The volume name cannot contain the
+\*(Aq:\*(Aq
+character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
+.PP
+path = \fIPATH\fR \fB(V)\fR
+.RS 4
+The path name must be a fully qualified path name\&.
+.RE
+.PP
+appledouble = \fIea|v2\fR \fB(V)\fR
+.RS 4
+Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble v2, the new default format is
+\fBea\fR\&.
+.RE
+.PP
+vol size limit = \fIsize in MiB\fR \fB(V)\fR
+.RS 4
+Useful for Time Machine: limits the reported volume size, thus preventing Time Machine from using the whole real disk space for backup\&. Example: "vol size limit = 1000" would limit the reported disk space to 1 GB\&.
+\fBIMPORTANT: \fR
+This is an approximated calculation taking into account the contents of Time Machine sparsebundle images\&. Therefor you MUST NOT use this volume to store other content when using this option, because it would NOT be accounted\&. The calculation works by reading the band size from the Info\&.plist XML file of the sparsebundle, reading the bands/ directory counting the number of band files, and then multiplying one with the other\&.
+.RE
+.PP
+valid users = \fIuser @group\fR \fB(V)\fR
+.RS 4
+The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by spaces or commas\&. Groups are designated by a @ prefix\&. Names may be quoted in order to allow for spaces in names\&. Example:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+valid users = user "user 2" @group \(lq@group 2"
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
+invalid users = \fIusers/groups\fR \fB(V)\fR
+.RS 4
+The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the "valid users" option\&.
+.RE
+.PP
+hosts allow = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
+.RS 4
+Only listed hosts and networks are allowed, all others are rejected\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.sp
+Example: hosts allow = 10\&.1\&.0\&.0/16 10\&.2\&.1\&.100 2001:0db8:1234::/48
+.RE
+.PP
+hosts deny = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
+.RS 4
+Listed hosts and nets are rejected, all others are allowed\&.
+.sp
+Example: hosts deny = 192\&.168\&.100/24 10\&.1\&.1\&.1 2001:db8::1428:57ab
+.RE
+.PP
+cnid scheme = \fIbackend\fR \fB(V)\fR
+.RS 4
+set the CNID backend to be used for the volume, default is [@DEFAULT_CNID_SCHEME@] available schemes: [@compiled_backends@]
+.RE
+.PP
+ea = \fInone|auto|sys|ad\fR \fB(V)\fR
+.RS 4
+Specify how Extended Attributes
+are stored\&.
+\fBauto\fR
+is the default\&.
+.PP
+auto
+.RS 4
+Try
+\fBsys\fR
+(by setting an EA on the shared directory itself), fallback to
+\fBad\fR\&. Requires writable volume for performing test\&. "\fBread only = yes\fR" overwrites
+\fBauto\fR
+with
+\fBnone\fR\&. Use explicit "\fBea = sys|ad\fR" for read\-only volumes where appropriate\&.
+.RE
+.PP
+sys
+.RS 4
+Use filesystem Extended Attributes\&.
+.RE
+.PP
+ad
+.RS 4
+Use files in
+\fI\&.AppleDouble\fR
+directories\&.
+.RE
+.PP
+none
+.RS 4
+No Extended Attributes support\&.
+.RE
+.RE
+.PP
+mac charset = \fICHARSET\fR \fB(V)\fR
+.RS 4
+specifies the Mac client charset for this Volume, e\&.g\&.
+\fIMAC_ROMAN\fR,
+\fIMAC_CYRILLIC\fR\&. If not specified the global setting is applied\&. This setting is only required if you need volumes, where the Mac charset differs from the one globally set in the [Global] section\&.
+.RE
+.PP
+casefold = \fBoption\fR \fB(V)\fR
+.RS 4
+The casefold option handles, if the case of filenames should be changed\&. The available options are:
+.sp
+\fBtolower\fR
+\- Lowercases names in both directions\&.
+.sp
+\fBtoupper\fR
+\- Uppercases names in both directions\&.
+.sp
+\fBxlatelower\fR
+\- Client sees lowercase, server sees uppercase\&.
+.sp
+\fBxlateupper\fR
+\- Client sees uppercase, server sees lowercase\&.
+.RE
+.PP
+password = \fIpassword\fR \fB(V)\fR
+.RS 4
+This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
+.RE
+.PP
+file perm = \fImode\fR \fB(V)\fR, directory perm = \fImode\fR \fB(V)\fR
+.RS 4
+Add(or) with the client requested permissions:
+\fBfile perm\fR
+is for files only,
+\fBdirectory perm\fR
+is for directories only\&. Don\*(Aqt use with "\fBunix priv = no\fR"\&.
+.PP
+\fBExample.\ \&Volume for a collaborative workgroup\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+file perm = 0660 directory perm =
+              0770
+.fi
+.if n \{\
+.RE
+.\}
+
+.RE
+.PP
+umask = \fImode\fR \fB(V)\fR
+.RS 4
+set perm mask\&. Don\*(Aqt use with "\fBunix priv = no\fR"\&.
+.RE
+.PP
+preexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
+postexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run when the volume is closed, ignored for user defined volumes
+.RE
+.PP
+root preexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run as root when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
+root postexec = \fIcommand\fR \fB(V)\fR
+.RS 4
+command to be run as root when the volume is closed, ignored for user defined volumes
+.RE
+.PP
+rolist = \fBusers/groups\fR \fB(V)\fR
+.RS 4
+Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
+.RE
+.PP
+rwlist = \fIusers/groups\fR \fB(V)\fR
+.RS 4
+Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
+.RE
+.PP
+veto files = \fIvetoed names\fR \fB(V)\fR
+.RS 4
+hide files and directories,where the path matches one of the \*(Aq/\*(Aq delimited vetoed names\&. The veto string must always be terminated with a \*(Aq/\*(Aq, eg\&. "veto1/", "veto1/veto2/"\&.
+.RE
+.SS "Volume options"
+.PP
+Boolean volume options\&.
+.PP
+acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to flag volumes as supporting ACLs\&. If ACL support is compiled in, this is yes by default\&.
+.RE
+.PP
+cnid dev = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to use the device number in the CNID backends\&. Helps when the device number is not constant across a reboot, eg cluster, \&.\&.\&.
+.RE
+.PP
+convert appledouble = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether automatic conversion from
+\fBappledouble = v2\fR
+to
+\fBappledouble = ea\fR
+is performed when accessing filesystems from clients\&. This is generally useful, but costs some performance\&. It\*(Aqs recommendable to run
+\fBdbd\fR
+on volumes and do the conversion with that\&. Then this option can be set to no\&.
+.RE
+.PP
+follow symlinks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+The default setting is false thus symlinks are not followed on the server\&. This is the same behaviour as OS X\*(Aqs AFP server\&. Setting the option to true causes afpd to follow symlinks on the server\&. symlinks may point outside of the AFP volume, currently afpd doesn\*(Aqt do any checks for "wide symlinks"\&.
+.RE
+.PP
+invisible dots = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+make dot files invisible\&. WARNING: enabling this option will lead to unwanted sideeffects were OS X applications when saving files to a temporary file starting with a dot first, then renaming the temp file to its final name, result in the saved file being invisible\&. The only thing this option is useful for is making files that start with a dot invisible on Mac OS 9\&. It\*(Aqs completely useless on Mac OS X, as both in Finder and in Terminal files starting with a dot are hidden anyway\&.
+.RE
+.PP
+network ids = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether the server support network ids\&. Setting this to
+\fIno\fR
+will result in the client not using ACL AFP functions\&.
+.RE
+.PP
+preexec close = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+A non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+read only = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Specifies the share as being read only for all users\&. Overwrites
+\fBea = auto\fR
+with
+\fBea = none\fR
+.RE
+.PP
+root preexec close= \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+A non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+search db = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
+.RE
+.PP
+stat vol = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
+.RE
+.PP
+time machine = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+Whether to enable Time Machine support for this volume\&.
+.RE
+.PP
+unix priv = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
+.RS 4
+Whether to use AFP3 UNIX privileges\&. This should be set for OS X clients\&. See also:
+\fBfile perm\fR,
+\fBdirectory perm\fR
+and
+\fBumask\fR\&.
+.RE
+.SH "CNID BACKENDS"
+.PP
+The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\*(Aqs in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
+@localstatedir@/netatalk/CNID/(volumename)/\&.AppleDB/
+directory\&.
+.PP
+cdb
+.RS 4
+"Concurrent database", backend is based on Oracle Berkley DB\&. With this backend several
+\fBafpd\fR
+daemons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
+\fBafpd\fR
+process is active for a volume\&. The drawback is, that the crash of a single
+\fBafpd\fR
+process might corrupt the database\&.
+.RE
+.PP
+dbd
+.RS 4
+Access to the CNID database is restricted to the
+\fBcnid_metad\fR
+daemon process\&.
+\fBafpd\fR
+processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
+\fBcdb\fR
+.RE
+.PP
+last
+.RS 4
+This backend is an exception, in terms of ID persistency\&. ID\*(Aqs are only valid for the current session\&. This is basically what
+\fBafpd\fR
+did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&. Starting with Netatalk 3\&.0, it becomes the
+\fIread only mode\fR
+automatically\&.
+.sp
+\fBWarning\fR: It is
+\fINOT\fR
+recommended to use this backend for volumes anymore, as
+\fBafpd\fR
+now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
+.RE
+.PP
+Even though
+\fB\&./configure \-\-help\fR
+might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\*(Aqt use them unless you know what you\*(Aqre doing, they may be removed without further notice from future versions\&.
+.SH "CHARSET OPTIONS"
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
+.PP
+\fBafpd\fR
+needs a way to preserve extended Macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0xF0) was saved as
+:f0\&. Some special characters will be converted as to :xx notation as well\&. \*(Aq/\*(Aq will be encoded to
+:2f, if
+\fBusedots\fR
+is not specified, a leading dot \*(Aq\&.\*(Aq will be encoded as
+:2e\&.
+.PP
+This version now uses UTF\-8 as the default encoding for names\&. \*(Aq/\*(Aq will be converted to \*(Aq:\*(Aq\&.
+.PP
+The
+\fBvol charset\fR
+option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be vol charset ISO\-8859\-15\&.
+\fBafpd\fR
+will accept any
+\fBiconv\fR(1)
+provided charset\&. If a character cannot be converted from the
+\fBmac charset\fR
+to the selected
+\fBvol charset\fR, afpd will save it as a CAP encoded character\&. For AFP3 clients,
+\fBafpd\fR
+will convert the UTF\-8
+character to
+\fBmac charset\fR
+first\&. If this conversion fails, you\*(Aqll receive a \-50 error on the mac\&.
+.PP
+\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafppasswd\fR(5),
+\fBafp_signature.conf\fR(5),
+\fBextmap.conf\fR(5),
+\fBcnid_metad\fR(8)
diff --git a/man/man5/afp.conf.5.tmpl b/man/man5/afp.conf.5.tmpl
deleted file mode 100644 (file)
index 50e6f81..0000000
+++ /dev/null
@@ -1,1117 +0,0 @@
-'\" t
-.\"     Title: afp.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 25 Jul 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP\&.CONF" "5" "25 Jul 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp.conf \- Netatalk configuration file
-.SH "SYNOPSIS"
-.PP
-The
-afp\&.conf
-file is the configuration file for the
-\fBNetatalk\fR
-AFP file server\&.
-.PP
-All AFP specific configuration and AFP volume definitions are done via this file\&.
-.SH "FILE FORMAT"
-.PP
-The file consists of sections and parameters\&. A section begins with the name of the section in square brackets and continues until the next section begins\&. Sections contain parameters of the form:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIname\fR = \fIvalue \fR
-      
-.fi
-.if n \{\
-.RE
-.\}
-.PP
-The file is line\-based \- that is, each newline\-terminated line represents either a comment, a section name or a parameter\&.
-.PP
-Section and parameter names are case sensitive\&.
-.PP
-Only the first equals sign in a parameter is significant\&. Whitespace before or after the first equals sign is discarded\&. Leading, trailing and internal whitespace in section and parameter names is irrelevant\&. Leading and trailing whitespace in a parameter value is discarded\&. Internal whitespace within a parameter value is retained verbatim\&.
-.PP
-Any line beginning with a semicolon (\(lq;\(rq) or a hash (\(lq#\(rq) character is ignored, as are lines containing only whitespace\&.
-.PP
-Any line ending in a
-\(lq\e\(rq
-is continued on the next line in the customary UNIX fashion\&.
-.PP
-The values following the equals sign in parameters are all either a string (no quotes needed) or a boolean, which may be given as yes/no, 1/0 or true/false\&. Case is not significant in boolean values, but is preserved in string values\&. Some items such as create masks are numeric\&.
-.PP
-The parameter
-\fBinclude = \fR\fB\fIpath\fR\fR
-allows you to include one config file inside another\&. The file is included literally, as though typed in place\&. Nested includes are not supported\&.
-.SH "SECTION DESCRIPTIONS"
-.PP
-Each section in the configuration file (except for the [Global] section) describes a shared resource (known as a
-\(lqvolume\(rq)\&. The section name is the name of the volume and the parameters within the section define the volume attributes and options\&.
-.PP
-There are two special sections, [Global] and [Homes], which are described under
-\fIspecial sections\fR\&. The following notes apply to ordinary section descriptions\&.
-.PP
-A volume consists of a directory to which access is being given plus a description of the access rights which are granted to the user of the service\&. For volumes the
-\fBpath\fR
-option must specify the directory to share\&.
-.PP
-Any volume section without
-\fBpath\fR
-option is considered a
-\fIvol preset\fR
-which can be selected in other volume sections via the
-\fBvol preset\fR
-option and constitutes defaults for the volume\&. For any option speficied both in a preset
-\fIand\fR
-in a volume section the volume section setting completly substitutes the preset option\&.
-.PP
-The access rights granted by the server are masked by the access rights granted to the specified or guest UNIX user by the host system\&. The server does not grant more access than the host system grants\&.
-.PP
-The following sample section defines an AFP volume\&. The user has full access to the path
-/foo/bar\&. The share is accessed via the share name
-baz:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-[baz]
-path = /foo/bar
-      
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SPECIAL SECTIONS"
-.SS "The [Global] section"
-.PP
-Parameters in this section apply to the server as a whole\&. Parameters denoted by a (G) below are must be set in this section\&.
-.SS "The [Homes] section"
-.PP
-This section enable sharing of the UNIX server user home directories\&. Specifying an optional
-\fBpath\fR
-parameter means that not the whole user home will be shared but the subdirectory
-\fBpath\fR\&. It is neccessary to define the
-\fBbasedir regex\fR
-option\&. It should be a regex which matches the parent directory of the user homes\&. Parameters denoted by a (H) belong to volume sections\&. The optional parameter
-\fBhome name\fR
-can be used to change the AFP volume name which
-\fI$u\'s home\fR
-by default\&. See below under VARIABLE SUBSTITUTIONS\&.
-.PP
-The following example illustrates this\&. Given all user home directories are stored under
-/home:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-[Homes]
-path = afp\-data
-basedir regex = /home
-      
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For a user
-\fIjohn\fR
-this results in an AFP home volume with a path of
-/home/john/afp\-data\&.
-.SH "PARAMETERS"
-.PP
-Parameters define the specific attributes of sections\&.
-.PP
-Some parameters are specific to the [Global] section (e\&.g\&.,
-\fIlog type\fR)\&. All others are permissible only in volume sections\&. The letter
-\fIG\fR
-in parentheses indicates that a parameter is specific to the [Global] section\&. The letter
-\fIV\fR
-indicates that a parameter can be specified in a volume specific section\&.
-.SH "VARIABLE SUBSTITUTIONS"
-.PP
-You can use variables in volume names\&. The use of variables in paths is not supported for now\&.
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 1.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  1." 4.2
-.\}
-if you specify an unknown variable, it will not get converted\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 2.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  2." 4.2
-.\}
-if you specify a known variable, but that variable doesn\'t have a value, it will get ignored\&.
-.RE
-.PP
-The variables which can be used for substitutions are:
-.PP
-$b
-.RS 4
-basename
-.RE
-.PP
-$c
-.RS 4
-client\'s ip address
-.RE
-.PP
-$d
-.RS 4
-volume pathname on server
-.RE
-.PP
-$f
-.RS 4
-full name (contents of the gecos field in the passwd file)
-.RE
-.PP
-$g
-.RS 4
-group name
-.RE
-.PP
-$h
-.RS 4
-hostname
-.RE
-.PP
-$i
-.RS 4
-client\'s ip, without port
-.RE
-.PP
-$s
-.RS 4
-server name (this can be the hostname)
-.RE
-.PP
-$u
-.RS 4
-user name (if guest, it is the user that guest is running as)
-.RE
-.PP
-$v
-.RS 4
-volume name
-.RE
-.PP
-$$
-.RS 4
-prints dollar sign ($)
-.RE
-.SH "EXPLANATION OF GLOBAL PARAMETERS"
-.SS "Authentication Options"
-.PP
-admin auth user = \fIuser\fR \fB(G)\fR
-.RS 4
-Specifying eg "\fBadmin auth user = root\fR" whenever a normal user login fails, afpd will try to authenticate as the specified
-\fBadmin auth user\fR\&. If this succeeds, a normal session is created for the original connecting user\&. Said differently: if you know the password of
-\fBadmin auth user\fR, you can authenticate as any other user\&.
-.RE
-.PP
-k5 keytab = \fIpath\fR \fB(G)\fR, k5 service = \fIservice\fR \fB(G)\fR, k5 realm = \fIrealm\fR \fB(G)\fR
-.RS 4
-These are required if the server supports the Kerberos 5 authentication UAM\&.
-.RE
-.PP
-ldap auth method = \fInone|simple|sasl\fR \fB(G)\fR
-.RS 4
-Authentication method:
-\fBnone | simple | sasl\fR
-.PP
-none
-.RS 4
-anonymous LDAP bind
-.RE
-.PP
-simple
-.RS 4
-simple LDAP bind
-.RE
-.PP
-sasl
-.RS 4
-SASL\&. Not yet supported !
-.RE
-.RE
-.PP
-ldap auth dn = \fIdn\fR \fB(G)\fR
-.RS 4
-Distinguished Name of the user for simple bind\&.
-.sp
-.RE
-.PP
-ldap auth pw = \fIpassword\fR \fB(G)\fR
-.RS 4
-Distinguished Name of the user for simple bind\&.
-.sp
-.RE
-.PP
-ldap server = \fIhost\fR \fB(G)\fR
-.RS 4
-Name or IP address of your LDAP Server\&. This is only needed for explicit ACL support in order to be able to query LDAP for UUIDs\&.
-.sp
-You can use
-\fBafpldaptest\fR(1)
-to syntactically check your config\&.
-.RE
-.PP
-ldap userbase = \fIbase dn\fR \fB(G)\fR
-.RS 4
-DN of the user container in LDAP\&.
-.sp
-.RE
-.PP
-ldap userscope = \fIscope\fR \fB(G)\fR
-.RS 4
-Search scope for user search:
-\fBbase | one | sub\fR
-.sp
-.RE
-.PP
-ldap groupbase = \fIbase dn\fR \fB(G)\fR
-.RS 4
-DN of the group container in LDAP\&.
-.sp
-.RE
-.PP
-ldap groupscope = \fIscope\fR \fB(G)\fR
-.RS 4
-Search scope for user search:
-\fBbase | one | sub\fR
-.sp
-.RE
-.PP
-ldap uuid attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the UUIDs\&.
-.sp
-Note: this is used both for users and groups\&.
-.sp
-.RE
-.PP
-ldap name attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the users short name\&.
-.sp
-.RE
-.PP
-ldap uuid string = \fISTRING\fR \fB(G)\fR
-.RS 4
-Format of the uuid string in the directory\&. A series of x and \-, where every x denotes a value 0\-9a\-f and every \- is a seperator\&.
-.sp
-Default: xxxxxxxx\-xxxx\-xxxx\-xxxx\-xxxxxxxxxxxx
-.RE
-.PP
-ldap uuid encoding = \fIstring | ms\-guid (default: string)\fR \fB(G)\fR
-.RS 4
-Format of the UUID of the LDAP attribute, allows usage of the binary objectGUID fields from Active Directory\&. If left unspecified, string is the default, which passes through the ASCII UUID returned by most other LDAP stores\&. If set to ms\-guid, the internal UUID representation is converted to and from the binary format used in the objectGUID attribute found on objects in Active Directory when interacting with the server\&.
-.PP
-string
-.RS 4
-UUID is a string, use with eg OpenDirectory\&.
-.RE
-.PP
-ms\-guid
-.RS 4
-Binary objectGUID from Active Directory
-.RE
-.RE
-.PP
-ldap group attr = \fIdn\fR \fB(G)\fR
-.RS 4
-Name of the LDAP attribute with the groups short name\&.
-.sp
-.RE
-.PP
-nt domain = \fIDOMAIN\fR \fB(G)\fR, nt separator = \fISEPERATOR\fR \fB(G)\fR
-.RS 4
-Use for eg\&. winbind authentication, prepends both strings before the username from login and then tries to authenticate with the result through the availabel and active UAM authentication modules\&.
-.RE
-.PP
-save password = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Enables or disables the ability of clients to save passwords locally\&.
-.RE
-.PP
-set password = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Enables or disables the ability of clients to change their passwords via chooser or the "connect to server" dialog\&.
-.RE
-.PP
-uam list = \fIuam list\fR \fB(G)\fR
-.RS 4
-Space or comma separated list of UAMs\&. (The default is "uams_dhx\&.so uams_dhx2\&.so")\&.
-.sp
-The most commonly used UAMs are:
-.PP
-uams_guest\&.so
-.RS 4
-allows guest logins
-.RE
-.PP
-uams_clrtxt\&.so
-.RS 4
-(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&. (legacy)
-.RE
-.PP
-uams_randum\&.so
-.RS 4
-allows Random Number and Two\-Way Random Number Exchange for authentication (requires a separate file containing the passwords, either :ETCDIR:/afppasswd file or the one specified via "\fBpasswd file\fR"\&. See
-\fBafppasswd\fR(1)
-for details\&. (legacy)
-.RE
-.PP
-uams_dhx\&.so
-.RS 4
-(uams_dhx_pam\&.so or uams_dhx_passwd\&.so) Allow Diffie\-Hellman eXchange (DHX) for authentication\&.
-.RE
-.PP
-uams_dhx2\&.so
-.RS 4
-(uams_dhx2_pam\&.so or uams_dhx2_passwd\&.so) Allow Diffie\-Hellman eXchange 2 (DHX2) for authentication\&.
-.RE
-.PP
-uam_gss\&.so
-.RS 4
-Allow Kerberos V for authentication (optional)
-.RE
-.RE
-.PP
-uam path = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the default path for UAMs for this server (default is :LIBDIR:/netatalk)\&.
-.RE
-.SS "Charset Options"
-.PP
-With OS X Apple introduced the AFP3 protocol\&. One of the big changes was, that AFP3 uses Unicode names encoded as Decomposed UTF\-8 (UTF8\-MAC)\&. Previous AFP/OS versions used charsets like MacRoman, MacCentralEurope, etc\&.
-.PP
-To be able to serve AFP3 and older clients at the same time,
-\fBafpd\fR
-needs to be able to convert between UTF\-8 and Mac charsets\&. Even OS X clients partly still rely on the mac charset\&. As there\'s no way,
-\fBafpd\fR
-can detect the codepage a pre AFP3 client uses, you have to specify it using the
-\fBmac charset\fR
-option\&. The default is MacRoman, which should be fine for most western users\&.
-.PP
-As
-\fBafpd\fR
-needs to interact with UNIX operating system as well, it need\'s to be able to convert from UTF8\-MAC / Mac charset to the UNIX charset\&. By default
-\fBafpd\fR
-uses
-\fIUTF8\fR\&. You can set the UNIX charset using the
-\fBunix charset\fR
-option\&. If you\'re using extended characters in the configuration files for
-\fBafpd\fR, make sure your terminal matches the
-\fBunix charset\fR\&.
-.PP
-mac charset = \fICHARSET\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the Mac clients charset, e\&.g\&.
-\fIMAC_ROMAN\fR\&. This is used to convert strings and filenames to the clients codepage for OS9 and Classic, i\&.e\&. for authentication and AFP messages (SIGUSR2 messaging)\&. This will also be the default for the volumes
-\fBmac charset\fR\&. Defaults to
-\fIMAC_ROMAN\fR\&.
-.RE
-.PP
-unix charset = \fICHARSET\fR \fB(G)\fR
-.RS 4
-Specifies the servers unix charset, e\&.g\&.
-\fIISO\-8859\-15\fR
-or
-\fIEUC\-JP\fR\&. This is used to convert strings to/from the systems locale, e\&.g\&. for authenthication, server messages and volume names\&. If
-\fILOCALE\fR
-is set, the systems locale is used\&. Defaults to
-\fIUTF8\fR\&.
-.RE
-.PP
-vol charset = \fICHARSET\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the encoding of the volumes filesystem\&. By default, it is the same as
-\fBunix charset\fR\&.
-.RE
-.SS "Password Options"
-.PP
-passwd file = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the path to the Randnum UAM passwd file for this server (default is :ETCDIR:/afppasswd)\&.
-.RE
-.PP
-passwd minlen = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the minimum password length, if supported by the UAM
-.RE
-.SS "Network Options"
-.PP
-advertise ssh = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Allows old Mac OS X clients (10\&.3\&.3\-10\&.4) to automagically establish a tunneled AFP connection through SSH\&. If this option is set, the server\'s answers to client\'s FPGetSrvrInfo requests contain an additional entry\&. It depends on both client\'s settings and a correctly configured and running
-\fBsshd\fR(8)
-on the server to let things work\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-Setting this option is not recommended since globally encrypting AFP connections via SSH will increase the server\'s load significantly\&. On the other hand, Apple\'s client side implementation of this feature in MacOS X versions prior to 10\&.3\&.4 contained a security flaw\&.
-.sp .5v
-.RE
-.RE
-.PP
-afp listen = \fIip address[:port] [ip adress[:port] \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specifies the IP address that the server should advertise
-\fBand\fR
-listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
-.RE
-.PP
-afp port = \fIport number\fR \fB(G)\fR
-.RS 4
-Allows a different TCP port to be used for AFP\&. The default is 548\&. Also sets the default port applied when none specified in an
-\fBafp listen\fR
-option\&.
-.RE
-.PP
-cnid listen = \fIip address[:port] [ip adress[:port] \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specifies the IP address that the CNID server should listen on\&. The default is
-\fBlocalhost:4700\fR\&.
-.RE
-.PP
-disconnect time = \fInumber\fR \fB(G)\fR
-.RS 4
-Keep disconnected AFP sessions for
-\fInumber\fR
-hours before dropping them\&. Default is 24 hours\&.
-.RE
-.PP
-dsireadbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Scale factor that determines the size of the DSI/TCP readahead buffer, default is 12\&. This is multiplies with the DSI server quantum (default ~300k) to give the size of the buffer\&. Increasing this value might increase throughput in fast local networks for volume to volume copies\&.
-\fINote\fR: This buffer is allocated per afpd child process, so specifying large values will eat up large amount of memory (buffer size * number of clients)\&.
-.RE
-.PP
-fqdn = \fIname:port\fR \fB(G)\fR
-.RS 4
-Specifies a fully\-qualified domain name, with an optional port\&. This is discarded if the server cannot resolve it\&. This option is not honored by AppleShare clients <= 3\&.8\&.3\&. This option is disabled by default\&. Use with caution as this will involve a second name resolution step on the client side\&. Also note that afpd will advertise this name:port combination but not automatically listen to it\&.
-.RE
-.PP
-hostname = \fIname\fR \fB(G)\fR
-.RS 4
-Use this instead of the result from calling hostname for dertermening which IP address to advertise, therfore the hostname is resolved to an IP which is the advertised\&. This is NOT used for listening and it is also overwritten by
-\fBafp listen\fR\&.
-.RE
-.PP
-max connections = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the maximum number of clients that can simultaneously connect to the server (default is 200)\&.
-.RE
-.PP
-server quantum = \fInumber\fR \fB(G)\fR
-.RS 4
-This specifies the DSI server quantum\&. The default value is 303840\&. The maximum value is 0xFFFFFFFFF, the minimum is 32000\&. If you specify a value that is out of range, the default value will be set\&. Do not change this value unless you\'re absolutely sure, what you\'re doing
-.RE
-.PP
-sleep time = \fInumber\fR \fB(G)\fR
-.RS 4
-Keep sleeping AFP sessions for
-\fInumber\fR
-hours before disconnecting clients in sleep mode\&. Default is 10 hours\&.
-.RE
-.PP
-tcprcvbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Try to set TCP receive buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
-.RE
-.PP
-tcpsndbuf = \fInumber\fR \fB(G)\fR
-.RS 4
-Try to set TCP send buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
-.RE
-.PP
-use sendfile = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to use sendfile
-syscall for sending file data to clients\&.
-.RE
-.PP
-zeroconf = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to use automatic Zeroconf
-service registration if Avahi or mDNSResponder were compiled in\&.
-.RE
-.SS "Miscellaneous Options"
-.PP
-admin group = \fIgroup\fR \fB(G)\fR
-.RS 4
-Allows users of a certain group to be seen as the superuser when they log in\&. This option is disabled by default\&.
-.RE
-.PP
-afp read locks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Whether to apply locks to the byte region read in FPRead calls\&. The AFP spec mandates this, but it\'s not really in line with UNIX semantics and is a performance hug\&.
-.RE
-.PP
-basedir regex = \fIregex\fR \fB(H)\fR
-.RS 4
-Regular expression which matches the parent directory of the user homes\&. In the simple case this is just a path ie
-\fBbasedir regex = /home\fR
-.RE
-.PP
-close vol = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Whether to close volumes possibly opened by clients when they\'re removed from the configuration and the configuration is reloaded\&.
-.RE
-.PP
-cnid server = \fIipaddress[:port]\fR \fB(G)/(V)\fR
-.RS 4
-Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
-.RE
-.PP
-dircachesize = \fInumber\fR \fB(G)\fR
-.RS 4
-Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
-.sp
-Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
-.RE
-.PP
-fce listener = \fIhost[:port]\fR \fB(G)\fR
-.RS 4
-Enables sending FCE events to the specified
-\fIhost\fR, default
-\fIport\fR
-is 12250 if not specified\&. Specifying mutliple listeners is done by having this option once for each of them\&.
-.RE
-.PP
-fce events = \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR \fB(G)\fR
-.RS 4
-Speficies which FCE events are active, default is
-\fIfmod,fdel,ddel,fcre,dcre\fR\&.
-.RE
-.PP
-fce coalesce = \fIall|delete|create\fR \fB(G)\fR
-.RS 4
-Coalesce FCE events\&.
-.RE
-.PP
-fce holdfmod = \fIseconds\fR \fB(G)\fR
-.RS 4
-This determines the time delay in seconds which is always waited if another file modification for the same file is done by a client before sending an FCE file modification event (fmod)\&. For example saving a file in Photoshop would generate multiple events by itself because the application is opening, modifying and closing a file mutliple times for every "save"\&. Defautl: 60 seconds\&.
-.RE
-.PP
-guest account = \fIname\fR \fB(G)\fR
-.RS 4
-Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.
-.RE
-.PP
-home name = \fIname\fR \fB(H)\fR
-.RS 4
-AFP user home volume name\&. The default is
-\fIusers\'s home\fR\&.
-.RE
-.PP
-keep sessions = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-Enable "Continuous AFP Service"\&. This means restarting AFP and CNID service daemons master processes, but keeping the AFP session processes\&. This can be used to install (most) updates to Netatalk without interruping active AFP sessions\&. Existing AFP sessions will still run the version from before updating, but new AFP sessions will run the updated code\&. After enabling this option when sending SIGQUIT to the
-\fInetatalk\fR
-service controller process, the AFP and CNID daemons will exit and then the service controller will restart them\&. AFP session processes are notified of the master afpd shutdown, they will then sleep 15\-20 seconds and then try to reconnect their IPC channel to the master afpd process\&. The IPC channel between the AFP master service daemon and the AFP session child is used for keeping session state of AFP sessions in the AFP master process\&. The session state is needed when AFP clients experience eg network outages and try to reconnect to the AFP server\&.
-.RE
-.PP
-login message = \fImessage\fR \fB(G)/(V)\fR
-.RS 4
-Sets a message to be displayed when clients logon to the server\&. The message should be in
-\fBunix charset\fR
-and should be quoted\&. Extended characters are allowed\&.
-.RE
-.PP
-map acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Whether to map filesystem ACLs to effective permissions\&.
-.RE
-.PP
-mimic model = \fImodel\fR \fB(G)\fR
-.RS 4
-Specifies the icon model that appears on clients\&. Defaults to off\&. Examples: RackMac (same as Xserve), PowerBook, PowerMac, Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro, AppleTV1,1, AirPort\&.
-.RE
-.PP
-signature = <text> \fB(G)\fR
-.RS 4
-Specify a server signature\&. The maximum length is 16 characters\&. This option is useful for clustered environments, to provide fault isolation etc\&. By default, afpd generate signature and saving it to
-:STATEDIR:/netatalk/afp_signature\&.conf
-automatically (based on random number)\&. See also asip\-status\&.pl(1)\&.
-.RE
-.PP
-solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
-.RS 4
-Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
-.RE
-.PP
-vol dbpath = \fIpath\fR \fB(G)\fR
-.RS 4
-Sets the database information to be stored in path\&. You have to specifiy a writable location, even if the volume is read only\&. The default is
-:STATEDIR:/netatalk/CNID/\&.
-.RE
-.PP
-volnamelen = \fInumber\fR \fB(G)\fR
-.RS 4
-Max length of UTF8\-MAC volume name for Mac OS X\&. Note that Hangul is especially sensitive to this\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-73:  limit of Mac OS X 10\&.1
-80:  limit of Mac OS X 10\&.4/10\&.5 (default)
-255: limit of recent Mac OS X
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Mac OS 9 and earlier are not influenced by this, because Maccharset volume name is always limitted to 27 bytes\&.
-.RE
-.PP
-vol preset = \fIname\fR \fB(G)/(V)\fR
-.RS 4
-Use section
-\fBname\fR
-as option preset for all volumes (when set in the [Global] section) or for one volume (when set in that volume\'s section)\&.
-.RE
-.SS "Logging Options"
-.PP
-log file = \fIlogfile\fR \fB(G)\fR
-.RS 4
-If not specified Netatalk logs to syslogs daemon facilify\&. Otherwise it logs to
-\fBlogfile\fR\&.
-.RE
-.PP
-log level = \fItype:level [type:level \&.\&.\&.]\fR \fB(G)\fR, log level = \fItype:level,[type:level, \&.\&.\&.]\fR \fB(G)\fR
-.RS 4
-Specify that any message of a loglevel up to the given
-\fBlog level\fR
-should be logged\&.
-.sp
-By default afpd logs to syslog with a default logging setup equivalent to
-\fBdefault:note\fR
-.sp
-logtypes: default, afpdaemon, logger, uamsdaemon
-.sp
-loglevels: severe, error, warn, note, info, debug, debug6, debug7, debug8, debug9, maxdebug
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-Both logtype and loglevels are case insensitive\&.
-.sp .5v
-.RE
-.RE
-.SS "Debug Parameters"
-.PP
-These options are useful for debugging only\&.
-.PP
-tickleval = \fInumber\fR \fB(G)\fR
-.RS 4
-Sets the tickle timeout interval (in seconds)\&. Defaults to 30\&.
-.RE
-.PP
-timeout = \fInumber\fR \fB(G)\fR
-.RS 4
-Specify the number of tickles to send before timing out a connection\&. The default is 4, therefore a connection will timeout after 2 minutes\&.
-.RE
-.PP
-client polling = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
-.RS 4
-With this option enabled, afpd won\'t advertise that it is capable of server notifications, so that connected clients poll the server every 10 seconds to detect changes in opened server windows\&.
-\fINote\fR: Depending on the number of simultaneously connected clients and the network\'s speed, this can lead to a significant higher load on your network!
-.sp
-Do not use this option any longer as present Netatalk correctly supports server notifications, allowing connected clients to update folder listings in case another client changed the contents\&.
-.RE
-.SH "EXPLANATION OF VOLUME PARAMETERS"
-.SS "Parameters"
-.PP
-The section name defines the volume name which is the name that appears in the Chooser or the "connect to server" dialog on Macintoshes to represent the appropriate share\&. No two volumes may have the same name\&. The volume name cannot contain the
-\':\'
-character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
-.PP
-path = \fIPATH\fR \fB(V)\fR
-.RS 4
-The path name must be a fully qualified path name, or a path name using either the ~ shell shorthand or any of the substitution variables, which are listed below\&.
-.sp
-The volume name is the name that appears in the Chooser ot the "connect to server" dialog on Macintoshes to represent the appropriate share\&. If volumename is unspecified, the last component of pathname is used\&. No two volumes may have the same name\&. If there are spaces in the name, it should be in quotes (i\&.e\&. "File Share")\&. The volume name cannot contain the
-\':\'
-character\&. The volume name is mangled if it is very long\&. Mac charset volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to volnamelen parameter\&.
-.RE
-.PP
-appledouble = \fIea|v2\fR \fB(V)\fR
-.RS 4
-Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble v2, the new default format is
-\fBea\fR\&.
-.RE
-.PP
-vol size limit = \fIsize in MiB\fR \fB(V)\fR
-.RS 4
-Useful for Time Machine: limits the reported volume size, thus preventing Time Machine from using the whole real disk space for backup\&. Example: "vol size limit = 1000" would limit the reported disk space to 1 GB\&.
-\fBIMPORTANT: \fR
-This is an approimated calculation taking into accout the contents of Time Machine sparsebundle images\&. Therefor you MUST NOT use this volume to store other content when using this option, because it would NOT be accounted\&. The calculation works by reading the band size from the Info\&.plist XML file of the sparsebundle, reading the bands/ directory counting the number of band files, and then multiplying one with the other\&.
-.RE
-.PP
-valid users = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by spaces or commas\&. Groups are designated by a @ prefix\&. Example: "valid users = user1 user2 @group"
-.RE
-.PP
-invalid users = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the "valid users" option\&.
-.RE
-.PP
-hosts allow = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
-.RS 4
-Only listed hosts and networks are allowed, all others are rejected\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
-.sp
-Example: hosts allow = 10\&.1\&.0\&.0/16 10\&.2\&.1\&.100 2001:0db8:1234::/48
-.RE
-.PP
-hosts deny = \fIIP host address/IP netmask bits [ \&.\&.\&. ]\fR \fB(V)\fR
-.RS 4
-Listed hosts and nets are rejected, all others are allowed\&.
-.sp
-Example: hosts deny = 192\&.168\&.100/24 10\&.1\&.1\&.1 2001:db8::1428:57ab
-.RE
-.PP
-cnid scheme = \fIbackend\fR \fB(V)\fR
-.RS 4
-set the CNID backend to be used for the volume, default is [:DEFAULT_CNID_SCHEME:] available schemes: [:COMPILED_BACKENDS:]
-.RE
-.PP
-ea = \fInone|auto|sys|ad\fR
-.RS 4
-Specify how Extended Attributes
-are stored\&.
-\fBauto\fR
-is the default\&.
-.PP
-auto
-.RS 4
-Try
-\fBsys\fR
-(by setting an EA on the shared directory itself), fallback to
-\fBad\fR\&. Requires writeable volume for perfoming test\&. "\fBread only = yes\fR" overwrites
-\fBauto\fR
-with
-\fBnone\fR\&. Use explicit "\fBea = sys|ad\fR" for read\-only volumes where appropiate\&.
-.RE
-.PP
-sys
-.RS 4
-Use filesystem Extended Attributes\&.
-.RE
-.PP
-ad
-.RS 4
-Use files in
-\fI\&.AppleDouble\fR
-directories\&.
-.RE
-.PP
-none
-.RS 4
-No Extended Attributes support\&.
-.RE
-.RE
-.PP
-mac charset = \fICHARSET\fR \fB(V)\fR
-.RS 4
-specifies the Mac client charset for this Volume, e\&.g\&.
-\fIMAC_ROMAN\fR,
-\fIMAC_CYRILLIC\fR\&. If not specified the global setting is applied\&. This setting is only required if you need volumes, where the Mac charset differs from the one globally set in the [Global] section\&.
-.RE
-.PP
-casefold = \fBoption\fR
-.RS 4
-The casefold option handles, if the case of filenames should be changed\&. The available options are:
-.sp
-\fBtolower\fR
-\- Lowercases names in both directions\&.
-.sp
-\fBtoupper\fR
-\- Uppercases names in both directions\&.
-.sp
-\fBxlatelower\fR
-\- Client sees lowercase, server sees uppercase\&.
-.sp
-\fBxlateupper\fR
-\- Client sees uppercase, server sees lowercase\&.
-.RE
-.PP
-password = \fIpassword\fR \fB(V)\fR
-.RS 4
-This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
-.RE
-.PP
-file perm = \fImode\fR \fB(V)\fR, directory perm = \fImode\fR \fB(V)\fR
-.RS 4
-Add(or) with the client requested permissions:
-\fBfile perm\fR
-is for files only,
-\fBdirectory perm\fR
-is for directories only\&. Don\'t use with "\fBunix priv = no\fR"\&.
-.PP
-\fBExample.\ \&Volume for a collaborative workgroup\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-file perm = 0660
-directory perm = 0770
-.fi
-.if n \{\
-.RE
-.\}
-
-.RE
-.PP
-umask = \fImode\fR \fB(V)\fR
-.RS 4
-set perm mask\&. Don\'t use with "\fBunix priv = no\fR"\&.
-.RE
-.PP
-preexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run when the volume is mounted, ignored for user defined volumes
-.RE
-.PP
-postexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run when the volume is closed, ignored for user defined volumes
-.RE
-.PP
-root preexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run as root when the volume is mounted, ignored for user defined volumes
-.RE
-.PP
-root postexec = \fIcommand\fR \fB(V)\fR
-.RS 4
-command to be run as root when the volume is closed, ignored for user defined volumes
-.RE
-.PP
-rolist = \fBusers/groups\fR \fB(V)\fR
-.RS 4
-Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
-.RE
-.PP
-rwlist = \fIusers/groups\fR \fB(V)\fR
-.RS 4
-Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
-.RE
-.PP
-veto files = \fIvetoed names\fR \fB(V)\fR
-.RS 4
-hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. The veto string must always be terminated with a \'/\', eg\&. "veto1/", "veto1/veto2/"\&.
-.RE
-.SS "Volume options"
-.PP
-Boolean volume options\&.
-.PP
-acls = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to flag volumes as supporting ACLs\&. If ACL support is compiled in, this is yes by default\&.
-.RE
-.PP
-cnid dev = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to use the device number in the CNID backends\&. Helps when the device number is not constant across a reboot, eg cluster, \&.\&.\&.
-.RE
-.PP
-convert appledouble = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether automatic conversion from
-\fBappledouble = v2\fR
-to
-\fBappledouble = ea\fR
-is performed when accessing filesystems from clients\&. This is generally useful, but costs some performance\&. It\'s recommdable to run
-\fBdbd\fR
-on volumes and do the conversion with that\&. Then this option can be set to no\&.
-.RE
-.PP
-invisible dots = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-make dot files invisible\&.
-.RE
-.PP
-network ids = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether the server support network ids\&. Setting this to
-\fIno\fR
-will result in the client not using ACL AFP functions\&.
-.RE
-.PP
-preexec close = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-A non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
-.RE
-.PP
-read only = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Specifies the share as being read only for all users\&. Overwrites
-\fBea = auto\fR
-with
-\fBea = none\fR
-.RE
-.PP
-root preexec close= \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-A non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
-.RE
-.PP
-search db = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
-.RE
-.PP
-stat vol = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
-.RE
-.PP
-time machine = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
-.RS 4
-Whether to enable Time Machine suport for this volume\&.
-.RE
-.PP
-unix priv = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(V)\fR
-.RS 4
-Whether to use AFP3 UNIX privileges\&. This should be set for OS X clients\&. See also:
-\fBfile perm\fR,
-\fBdirectory perm\fR
-and
-\fBumask\fR\&.
-.RE
-.SH "CNID BACKENDS"
-.PP
-The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\'s in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
-:STATEDIR:/netatalk/CNID/(volumename)/\&.AppleDB/
-directory\&.
-.PP
-cdb
-.RS 4
-"Concurrent database", backend is based on Oracle Berkely DB\&. With this backend several
-\fBafpd\fR
-deamons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
-\fBafpd\fR
-process is active for a volume\&. The drawback is, that the crash of a single
-\fBafpd\fR
-process might corrupt the database\&.
-.RE
-.PP
-dbd
-.RS 4
-Access to the CNID database is restricted to the
-\fBcnid_metad\fR
-daemon process\&.
-\fBafpd\fR
-processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
-\fBcdb\fR
-.RE
-.PP
-last
-.RS 4
-This backend is an exception, in terms of ID persistency\&. ID\'s are only valid for the current session\&. This is basically what
-\fBafpd\fR
-did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&. Starting with Netatalk 3\&.0, it becomes the
-\fIread only mode\fR
-automatically\&.
-.sp
-\fBWarning\fR: It is
-\fINOT\fR
-recommended to use this backend for volumes anymore, as
-\fBafpd\fR
-now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
-.RE
-.PP
-Even though
-\fB\&./configure \-\-help\fR
-might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\'t use them unless you know what you\'re doing, they may be removed without further notice from future versions\&.
-.SH "CHARSET OPTIONS"
-.PP
-With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
-.PP
-\fBafpd\fR
-needs a way to preserve extended macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0xF0) was saved as
-:f0\&. Some special characters will be converted as to :xx notation as well\&. \'/\' will be encoded to
-:2f, if
-\fBusedots\fR
-is not specified, a leading dot \'\&.\' will be encoded as
-:2e\&.
-.PP
-This version now uses UTF\-8 as the default encoding for names\&. \'/\' will be converted to \':\'\&.
-.PP
-The
-\fBvol charset\fR
-option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be vol charset ISO\-8859\-15\&.
-\fBafpd\fR
-will accept any
-\fBiconv\fR(1)
-provided charset\&. If a character cannot be converted from the
-\fBmac charset\fR
-to the selected
-\fBvol charset\fR, afpd will save it as a CAP encoded character\&. For AFP3 clients,
-\fBafpd\fR
-will convert the UTF\-8
-character to
-\fBmac charset\fR
-first\&. If this conversion fails, you\'ll receive a \-50 error on the mac\&.
-.PP
-\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafppasswd\fR(5),
-\fBafp_signature.conf\fR(5),
-\fBcnid_metad\fR(8)
diff --git a/man/man5/afp_signature.conf.5.in b/man/man5/afp_signature.conf.5.in
new file mode 100644 (file)
index 0000000..c620ef0
--- /dev/null
@@ -0,0 +1,86 @@
+'\" t
+.\"     Title: afp_signature.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP_SIGNATURE\&.CONF" "5" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp_signature.conf \- Configuration file used by afpd(8) to specify server signature
+.SH "DESCRIPTION"
+.PP
+@localstatedir@/netatalk/afp_signature\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify server signature automagically\&. The configuration lines are composed like:
+.PP
+\fI"server name"\fR
+\fIhexa\-string\fR
+.PP
+The first field is server name\&. Server names must be quoted if they contain spaces\&. The second field is the hexadecimal string of 32 characters for 16\-bytes server signature\&.
+.PP
+The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+Server Signature is unique 16\-bytes identifier used to prevent logging on to the same server twice\&.
+.PP
+Netatalk 2\&.0 and earlier generated server signature by using gethostid()\&. There was a problem that another servers have the same signature because the hostid is not unique enough\&.
+.PP
+Current netatalk generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
+.PP
+This file should not be thoughtlessly edited and be copied onto another server\&. If it wants to set the signature intentionally, use the option "signature =" in afp\&.conf\&. In this case, afp_signature\&.conf is not used\&.
+.sp .5v
+.RE
+.PP
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&afp_signature.conf\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# This is a comment\&.
+"My Server" 74A0BB94EC8C13988B2E75042347E528
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBasip-status.pl\fR(1)
diff --git a/man/man5/afp_signature.conf.5.tmpl b/man/man5/afp_signature.conf.5.tmpl
deleted file mode 100644 (file)
index 54e2267..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-'\" t
-.\"     Title: afp_signature.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP_SIGNATURE\&.CONF" "5" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_signature.conf \- Configuration file used by afpd(8) to specify server signature
-.SH "DESCRIPTION"
-.PP
-:STATEDIR:/netatalk/afp_signature\&.conf
-is the configuration file used by
-\fBafpd\fR
-to specify server signature automagically\&. The configuration lines are composed like:
-.PP
-\fI"server name"\fR
-\fIhexa\-string\fR
-.PP
-The first field is server name\&. Server names must be quoted if they contain spaces\&. The second field is the hexadecimal string of 32 characters for 16\-bytes server signature\&.
-.PP
-The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-Server Signature is unique 16\-bytes identifier used to prevent logging on to the same server twice\&.
-.PP
-Netatalk 2\&.0 and earlier generated server signature by using gethostid()\&. There was a problem that another servers have the same signature because the hostid is not unique enough\&.
-.PP
-Current netatalk generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
-.PP
-This file should not be thoughtlessly edited and be copied onto another server\&. If it wants to set the signature intentionally, use the option "signature =" in afp\&.conf\&. In this case, afp_signature\&.conf is not used\&.
-.sp .5v
-.RE
-.PP
-.SH "EXAMPLES"
-.PP
-\fBExample.\ \&afp_signature.conf\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-# This is a comment\&.
-"My Server" 74A0BB94EC8C13988B2E75042347E528
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5),
-\fBasip-status.pl\fR(1)
diff --git a/man/man5/afp_voluuid.conf.5.in b/man/man5/afp_voluuid.conf.5.in
new file mode 100644 (file)
index 0000000..21612dd
--- /dev/null
@@ -0,0 +1,87 @@
+'\" t
+.\"     Title: afp_voluuid.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFP_VOLUUID\&.CONF" "5" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp_voluuid.conf \- Configuration file used by afpd(8) to specify UUID for Time Machine volume
+.SH "DESCRIPTION"
+.PP
+@localstatedir@/netatalk/afp_voluuid\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify UUID of Time Machine volume automagically\&. The configuration lines are composed like:
+.PP
+\fI"volume name"\fR
+\fIuuid\-string\fR
+.PP
+The first field is volume name\&. Volume names must be quoted if they contain spaces\&. The second field is the 36 character hexadecimal ASCII string representation of a UUID\&.
+.PP
+The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+This UUID is advertised by Zeroconf in order to provide robust disambiguation of Time Machine volume\&.
+.PP
+The afpd generates the UUID from random numbers and saves it into afp_voluuid\&.conf, only when setting "time machine = yes" option in afp\&.conf\&.
+.PP
+This file should not be thoughtlessly edited and be copied onto another server\&.
+.sp .5v
+.RE
+.PP
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&afp_voluuid.conf three TM volumes on one netatalk\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# This is a comment\&.
+"Backup for John Smith" 1573974F\-0ABD\-69CC\-C40A\-8519B681A0E1
+"bob" 39A487F4\-55AA\-8240\-E584\-69AA01800FE9
+mary 6331E2D1\-446C\-B68C\-3066\-D685AADBE911
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBafp.conf\fR(5),
+\fBavahi-daemon\fR(8),
+\fBmDNSResponder\fR(8)
diff --git a/man/man5/afp_voluuid.conf.5.tmpl b/man/man5/afp_voluuid.conf.5.tmpl
deleted file mode 100644 (file)
index 8d3626d..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-'\" t
-.\"     Title: afp_voluuid.conf
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFP_VOLUUID\&.CONF" "5" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_voluuid.conf \- Configuration file used by afpd(8) to specify UUID for Time Machine volume
-.SH "DESCRIPTION"
-.PP
-:STATEDIR:/netatalk/afp_voluuid\&.conf
-is the configuration file used by
-\fBafpd\fR
-to specify UUID of Time Machine volume automagically\&. The configuration lines are composed like:
-.PP
-\fI"volume name"\fR
-\fIuuid\-string\fR
-.PP
-The first field is volume name\&. Volume names must be quoted if they contain spaces\&. The second field is the 36 character hexadecimal ASCII string representation of a UUID\&.
-.PP
-The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-This UUID is advertised by Zeroconf in order to provide robust disambiguation of Time Machine volume\&.
-.PP
-The afpd generates the UUID from random numbers and saves it into afp_voluuid\&.conf, only when setting "time machine = yes" option in afp\&.conf\&.
-.PP
-This file should not be thoughtlessly edited and be copied onto another server\&.
-.sp .5v
-.RE
-.PP
-.SH "EXAMPLES"
-.PP
-\fBExample.\ \&afp_voluuid.conf three TM volumes on one netatalk\fR
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-# This is a comment\&.
-"Backup for John Smith" 1573974F\-0ABD\-69CC\-C40A\-8519B681A0E1
-"bob" 39A487F4\-55AA\-8240\-E584\-69AA01800FE9
-mary 6331E2D1\-446C\-B68C\-3066\-D685AADBE911
-.fi
-.if n \{\
-.RE
-.\}
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBafp.conf\fR(5),
-\fBavahi-daemon\fR(8),
-\fBmDNSResponder\fR(8)
diff --git a/man/man5/extmap.conf.5.in b/man/man5/extmap.conf.5.in
new file mode 100644 (file)
index 0000000..9e0abfa
--- /dev/null
@@ -0,0 +1,77 @@
+'\" t
+.\"     Title: extmap.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "EXTMAP\&.CONF" "5" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+extmap.conf \- Configuration file used by afpd(8) to specify file name extension mappings\&.
+.SH "SYNOPSIS"
+.HP \w'\fB@pkgconfdir@/extmap\&.conf\fR\fB\fR\ 'u
+\fB@pkgconfdir@/extmap\&.conf\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+
+@pkgconfdir@/extmap\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify file name extension mappings\&.
+.PP
+The configuration lines are composed like:
+.PP
+\&.extension
+\fI[ type [ creator ] ]\fR
+.PP
+Any line beginning with a hash (\(lq#\(rq) character is ignored\&. The leading\-dot lines specify file name extension mappings\&. The extension \*(Aq\&.\*(Aq sets the default creator and type for otherwise untyped Unix files\&.
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&Extension is jpg. Type is "JPEG". Creator is "ogle".\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\&.jpg "JPEG" "ogle"
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+\fBExample.\ \&Extension is lzh. Type is "LHA ". Creator is not defined.\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\&.lzh "LHA "
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafp.conf\fR(5),
+\fBafpd\fR(8)
index 711b8c366feeaa990bf572e2d9538b5f97a361d0..29043a09f888f3ffa7478bd8196c7134074373b5 100644 (file)
@@ -1,12 +1,3 @@
 Makefile
 Makefile.in
-afpd.8
-afp_acls.8
-atalkd.8
-cnid_dbd.8
-cnid_metad.8
-netatalk.8
-papd.8
-papstatus.8
-psf.8
-*.o
+*.8
index eb112ab3d7445ee15c824cea5b2df46e1d74d485..fdc6eaf190cfbcb2ec46753500fcfcde09ebf6cb 100644 (file)
@@ -1,24 +1,9 @@
 ## Makefile.am for man/man8/
 
-pkgconfdir = @PKGCONFDIR@
+man_MANS = \
+       afpd.8 \
+       cnid_dbd.8 \
+       cnid_metad.8 \
+       netatalk.8
 
-SUFFIXES = .tmpl .
-
-.tmpl:
-       sed -e s@:SBINDIR:@${sbindir}@ \
-           -e s@:BINDIR:@${bindir}@ \
-           -e s@:ETCDIR:@${pkgconfdir}@ \
-           -e s@:LIBDIR:@${libdir}@ \
-           -e s@:LIBEXECDIR:@${libexecdir}@ \
-           -e "s@:STATEDIR:@${localstatedir}@g" \
-           -e s@:NETATALK_VERSION:@${NETATALK_VERSION}@ \
-           <$< >$@
-
-GENERATED_MANS    = afpd.8 cnid_dbd.8 cnid_metad.8 netatalk.8
-TEMPLATE_FILES    = afpd.8.tmpl cnid_dbd.8.tmpl cnid_metad.8.tmpl netatalk.8.tmpl
-
-man_MANS = $(GENERATED_MANS)
-
-CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES)
+DISTCLEANFILES = $(man_MANS)
diff --git a/man/man8/afpd.8.in b/man/man8/afpd.8.in
new file mode 100644 (file)
index 0000000..ab40fa3
--- /dev/null
@@ -0,0 +1,171 @@
+'\" t
+.\"     Title: afpd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 19 Jan 2013
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "AFPD" "8" "19 Jan 2013" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpd \- Apple Filing Protocol daemon
+.SH "SYNOPSIS"
+.HP \w'\fBafpd\fR\fB\fR\fBafpd\fR\fB\fR\ 'u
+\fBafpd\fR\fB\fR [\-d] [\-F\ \fIconfigfile\fR]
+.br
+\fBafpd\fR\fB\fR \-v | \-V | \-h 
+.SH "DESCRIPTION"
+.PP
+\fBafpd\fR
+provides an Apple Filing Protocol (AFP) interface to the Unix file system\&. It is normally started at boot time by
+\fBnetatalk\fR(8)\&.
+.PP
+@pkgconfdir@/afp\&.conf
+is the configuration file used by
+\fBafpd\fR
+to determine the behavior and configuration of a file server\&.
+.SH "OPTIONS"
+.PP
+\-d
+.RS 4
+Specifies that the daemon should not fork\&.
+.RE
+.PP
+\-v
+.RS 4
+Print version information and exit\&.
+.RE
+.PP
+\-V
+.RS 4
+Print verbose information and exit\&.
+.RE
+.PP
+\-h
+.RS 4
+Print help and exit\&.
+.RE
+.PP
+\-F \fIconfigfile\fR
+.RS 4
+Specifies the configuration file to use\&. (Defaults to
+@pkgconfdir@/afp\&.conf\&.)
+.RE
+.SH "SIGNALS"
+.PP
+To shut down a user\*(Aqs
+\fBafpd\fR
+process it is recommended that
+\fBSIGKILL (\-9)\fR
+\fINOT\fR
+be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
+\fBafpd\fR
+is to send it a
+\fBSIGTERM (\-15)\fR
+signal and wait for it to die on its own\&.
+.PP
+SIGTERM and SIGUSR1 signals that are sent to the main
+\fBafpd\fR
+process are propagated to the children, so all will be affected\&.
+.PP
+SIGTERM
+.RS 4
+Clean exit\&. Propagates from master to childs\&.
+.RE
+.PP
+SIGQUIT
+.RS 4
+Send this to the master
+\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
+.RE
+.PP
+SIGHUP
+.RS 4
+Sending a
+\fBSIGHUP\fR
+to afpd will cause it to reload its configuration files\&.
+.RE
+.PP
+SIGINT
+.RS 4
+Sending a
+\fBSIGINT\fR
+to a child
+\fBafpd\fR
+enables
+\fImax_debug\fR
+logging for this process\&. The log is sent to the file
+/tmp/afpd\&.PID\&.XXXXXX\&. Sending another
+\fBSIGINT\fR
+will revert to the original log settings\&.
+.RE
+.PP
+SIGUSR1
+.RS 4
+The
+\fBafpd\fR
+process will send the message "The server is going down for maintenance\&." to the client and shut itself down in 5 minutes\&. New connections are not allowed\&. If this is sent to a child afpd, the other children are not affected\&. However, the main process will still exit, disabling all new connections\&.
+.RE
+.PP
+SIGUSR2
+.RS 4
+The
+\fBafpd\fR
+process will look in the message directory configured at build time for a file named message\&.pid\&. For each one found, a the contents will be sent as a message to the associated AFP client\&. The file is removed after the message is sent\&. This should only be sent to a child
+\fBafpd\fR\&.
+.RE
+.SH "FILES"
+.PP
+@pkgconfdir@/afp\&.conf
+.RS 4
+configuration file used by afpd
+.RE
+.PP
+@localstatedir@/netatalk/afp_signature\&.conf
+.RS 4
+list of server signature
+.RE
+.PP
+@localstatedir@/netatalk/afp_voluuid\&.conf
+.RS 4
+list of UUID for Time Machine volume
+.RE
+.PP
+@pkgconfdir@/extmap\&.conf
+.RS 4
+file name extension mapping
+.RE
+.PP
+@pkgconfdir@/msg/message\&.pid
+.RS 4
+contains messages to be sent to users\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBnetatalk\fR(8),
+\fBhosts_access\fR(5),
+\fBafp.conf\fR(5),
+\fBafp_signature.conf\fR(5),
+\fBafp_voluuid.conf\fR(5),
+\fBextmap.conf\fR(5),
+\fBdbd\fR(1)\&.
diff --git a/man/man8/afpd.8.tmpl b/man/man8/afpd.8.tmpl
deleted file mode 100644 (file)
index 3a91392..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-'\" t
-.\"     Title: afpd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "AFPD" "8" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afpd \- Apple Filing Protocol daemon
-.SH "SYNOPSIS"
-.HP \w'\fBafpd\fR\fB\fR\fBafpd\fR\fB\fR\ 'u
-\fBafpd\fR\fB\fR [\-d] [\-F\ \fIconfigfile\fR]
-.br
-\fBafpd\fR\fB\fR \-v | \-V | \-h 
-.SH "DESCRIPTION"
-.PP
-\fBafpd\fR
-provides an Apple Filing Protocol (AFP) interface to the Unix file system\&. It is normally started at boot time by
-\fBnetatalk\fR(8)\&.
-.PP
-:ETCDIR:/afp\&.conf
-is the configuration file used by
-\fBafpd\fR
-to determine the behavior and configuration of a file server\&.
-.SH "OPTIONS"
-.PP
-\-d
-.RS 4
-Specifies that the daemon should not fork\&.
-.RE
-.PP
-\-v
-.RS 4
-Print version information and exit\&.
-.RE
-.PP
-\-V
-.RS 4
-Print verbose information and exit\&.
-.RE
-.PP
-\-h
-.RS 4
-Print help and exit\&.
-.RE
-.PP
-\-F \fIconfigfile\fR
-.RS 4
-Specifies the configuration file to use\&. (Defaults to
-:ETCDIR:/afp\&.conf\&.)
-.RE
-.SH "SIGNALS"
-.PP
-To shut down a user\'s
-\fBafpd\fR
-process it is recommended that
-\fBSIGKILL (\-9)\fR
-\fINOT\fR
-be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
-\fBafpd\fR
-is to send it a
-\fBSIGTERM (\-15)\fR
-signal and wait for it to die on its own\&.
-.PP
-SIGTERM and SIGUSR1 signals that are sent to the main
-\fBafpd\fR
-process are propagated to the children, so all will be affected\&.
-.PP
-SIGTERM
-.RS 4
-Clean exit\&. Propagates from master to childs\&.
-.RE
-.PP
-SIGQUIT
-.RS 4
-Send this to the master
-\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
-.RE
-.PP
-SIGHUP
-.RS 4
-Sending a
-\fBSIGHUP\fR
-to afpd will cause it to reload its configuration files\&.
-.RE
-.PP
-SIGINT
-.RS 4
-Sending a
-\fBSIGINT\fR
-to a child
-\fBafpd\fR
-enables
-\fImax_debug\fR
-logging for this process\&. The log is sent to fhe file
-/tmp/afpd\&.PID\&.XXXXXX\&. Sending another
-\fBSIGINT\fR
-will revert to the original log settings\&.
-.RE
-.PP
-SIGUSR1
-.RS 4
-The
-\fBafpd\fR
-process will send the message "The server is going down for maintenance\&." to the client and shut itself down in 5 minutes\&. New connections are not allowed\&. If this is sent to a child afpd, the other children are not affected\&. However, the main process will still exit, disabling all new connections\&.
-.RE
-.PP
-SIGUSR2
-.RS 4
-The
-\fBafpd\fR
-process will look in the message directory configured at build time for a file named message\&.pid\&. For each one found, a the contents will be sent as a message to the associated AFP client\&. The file is removed after the message is sent\&. This should only be sent to a child
-\fBafpd\fR\&.
-.RE
-.SH "FILES"
-.PP
-:ETCDIR:/afp\&.conf
-.RS 4
-configuration file used by afpd
-.RE
-.PP
-:STATEDIR:/netatalk/afp_signature\&.conf
-.RS 4
-list of server signature
-.RE
-.PP
-:STATEDIR:/netatalk/afp_voluuid\&.conf
-.RS 4
-list of UUID for Time Machine volume
-.RE
-.PP
-:ETCDIR:/msg/message\&.pid
-.RS 4
-contains messages to be sent to users\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBnetatalk\fR(8),
-\fBhosts_access\fR(5),
-\fBafp.conf\fR(5),
-\fBafp_signature.conf\fR(5),
-\fBafp_voluuid.conf\fR(5),
-\fBdbd\fR(1)\&.
diff --git a/man/man8/cnid_dbd.8.in b/man/man8/cnid_dbd.8.in
new file mode 100644 (file)
index 0000000..3b5b68a
--- /dev/null
@@ -0,0 +1,249 @@
+'\" t
+.\"     Title: cnid_dbd
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 01 Jan 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "CNID_DBD" "8" "01 Jan 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+cnid_dbd \- implement access to CNID databases through a dedicated daemon process
+.SH "SYNOPSIS"
+.HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\fBcnid_dbd\fR\fB\fR\ 'u
+\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR
+.br
+\fBcnid_dbd\fR\fB\fR \-v | \-V 
+.SH "DESCRIPTION"
+.PP
+\fBcnid_dbd\fR
+provides an interface for storage and retrieval of catalog node IDs (CNIDs) and related information to the
+\fBafpd\fR
+daemon\&. CNIDs are a component of Macintosh based file systems with semantics that map not easily onto Unix file systems\&. This makes separate storage in a database necessary\&.
+\fBcnid_dbd\fR
+is part of the
+\fBCNID backend\fR
+framework of
+\fBafpd\fR
+and implements the
+\fBdbd\fR
+backend\&.
+.PP
+\fBcnid_dbd\fR
+is never started via the command line or system startup scripts but only by the
+\fBcnid_metad\fR
+daemon\&. There is one instance of
+\fBcnid_dbd\fR
+per netatalk volume\&.
+.PP
+\fBcnid_dbd\fR
+uses the
+\fBBerkeley DB\fR
+database library and uses transactionally protected updates\&. The
+\fBdbd\fR
+backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
+.PP
+\fBcnid_dbd\fR
+inherits the effective userid and groupid from
+\fBcnid_metad\fR
+on startup, which is normally caused by
+\fBafpd\fR
+serving a netatalk volume to a client\&. It changes to the
+\fBBerkeley DB\fR
+database home directory
+\fIdbdir\fR
+that is associated with the volume\&. If the userid inherited from
+\fBcnid_metad\fR
+is 0 (root),
+\fBcnid_dbd\fR
+will change userid and groupid to the owner and group of the database home directory\&. Otherwise, it will continue to use the inherited values\&.
+\fBcnid_dbd\fR
+will then attempt to open the database and start serving requests using filedescriptor
+\fIclntfd\fR\&. Subsequent instances of
+\fBafpd\fR
+that want to access the same volume are redirected to the running
+\fBcnid_dbd\fR
+process by
+\fBcnid_metad\fR
+via the filedescriptor
+\fIctrlfd\fR\&.
+.PP
+\fBcnid_dbd\fR
+can be configured to run forever or to exit after a period of inactivity\&. If
+\fBcnid_dbd\fR
+receives a TERM or an INT signal it will exit cleanly after flushing dirty database buffers to disk and closing
+\fBBerkeley DB\fR
+database environments\&. It is safe to terminate
+\fBcnid_dbd\fR
+this way, it will be restarted when necessary\&. Other signals are not handled and will cause an immediate exit, possibly leaving the CNID database in an inconsistent state (no transactions) or losing recent updates during recovery (transactions)\&.
+.PP
+The
+\fBBerkeley DB\fR
+database subsystem will create files named log\&.xxxxxxxxxx in the database home directory
+\fIdbdir\fR, where xxxxxxxxxx is a monotonically increasing integer\&. These files contain the transactional database changes\&. They will be removed regularly, unless the
+\fBlogfile_autoremove\fR
+option is specified in the
+\fIdb_param\fR
+configuration file (see below) with a value of 0 (default 1)\&.
+.SH "OPTIONS"
+.PP
+\fB\-v, \-V\fR
+.RS 4
+Show version and exit\&.
+.RE
+.SH "CONFIGURATION"
+.PP
+\fBcnid_dbd\fR
+reads configuration information from the file
+\fIdb_param\fR
+in the database directory
+\fIdbdir\fR
+on startup\&. If the file does not exist or a parameter is not listed, suitable default values are used\&. The format for a single parameter is the parameter name, followed by one or more spaces, followed by the parameter value, followed by a newline\&. The following parameters are currently recognized:
+.PP
+\fBlogfile_autoremove\fR
+.RS 4
+If set to 0, unused Berkeley DB transactional logfiles (log\&.xxxxxxxxxx in the database home directory) are not removed on startup of
+\fBcnid_dbd\fR
+and on a regular basis\&. Default: 1\&.
+.RE
+.PP
+\fBcachesize\fR
+.RS 4
+Determines the size of the Berkeley DB cache in kilobytes\&. Default: 8192\&. Each
+\fBcnid_dbd\fR
+process grabs that much memory on top of its normal memory footprint\&. It can be used to tune database performance\&. The
+\fBdb_stat\fR
+utility with the
+\fB\-m\fR
+option that comes with Berkley DB can help you determine ether you need to change this value\&. The default is pretty conservative so that a large percentage of requests should be satisfied from the cache directly\&. If memory is not a bottleneck on your system you might want to leave it at that value\&. The
+\fBBerkeley DB Tutorial and Reference Guide\fR
+has a section
+\fBSelecting a cache size\fR
+that gives more detailed information\&.
+.RE
+.PP
+\fBflush_frequency\fR, \fBflush_interval\fR
+.RS 4
+\fIflush_frequency\fR
+(Default: 1000) and
+\fIflush_interval\fR
+(Default: 1800) control how often changes to the database are checkpointed\&. Both of these operations are performed if either i) more than
+\fIflush_frequency\fR
+requests have been received or ii) more than
+\fIflush_interval\fR
+seconds have elapsed since the last save/checkpoint\&. Be careful to check your harddisk configuration for on disk cache settings\&. Many IDE disks just cache writes as the default behaviour, so even flushing database files to disk will not have the desired effect\&.
+.RE
+.PP
+\fBfd_table_size\fR
+.RS 4
+is the maximum number of connections (filedescriptors) that can be open for
+\fBafpd\fR
+client processes in
+\fBcnid_dbd\&.\fR
+Default: 512\&. If this number is exceeded, one of the existing connections is closed and reused\&. The affected
+\fBafpd\fR
+process will transparently reconnect later, which causes slight overhead\&. On the other hand, setting this parameter too high could affect performance in
+\fBcnid_dbd\fR
+since all descriptors have to be checked in a
+\fBselect()\fR
+system call, or worse, you might exceed the per process limit of open file descriptors on your system\&. It is safe to set the value to 1 on volumes where only one
+\fBafpd\fR
+client process is expected to run, e\&.g\&. home directories\&.
+.RE
+.PP
+\fBidle_timeout\fR
+.RS 4
+is the number of seconds of inactivity before an idle
+\fBcnid_dbd\fR
+exits\&. Default: 600\&. Set this to 0 to disable the timeout\&.
+.RE
+.SH "UPDATING"
+.PP
+Note that the first version to appear
+\fIafter\fR
+Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\*(Aqt upgrade a 2\&.0\&.x version because that one didn\*(Aqt prepare the database\&.
+.PP
+In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Stop the to be upgraded old version of Netatalk
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Using the old BerkeleyDB utilities run
+\fBdb_recover \-h <path to \&.AppleDB>\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Using the new BerkeleyDB utilities run
+\fBdb_upgrade \-v \-h <path to \&.AppleDB> \-f cnid2\&.db\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Again using the new BerkeleyDB utilities run
+\fBdb_checkpoint \-1 \-h <path to \&.AppleDB>\fR
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Start the the new version of Netatalk
+.RE
+.SH "SEE ALSO"
+.PP
+\fBcnid_metad\fR(8),
+\fBafpd\fR(8),
+\fBdbd\fR(1)
diff --git a/man/man8/cnid_dbd.8.tmpl b/man/man8/cnid_dbd.8.tmpl
deleted file mode 100644 (file)
index 347921e..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-'\" t
-.\"     Title: cnid_dbd
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 01 Jan 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "CNID_DBD" "8" "01 Jan 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-cnid_dbd \- implement access to CNID databases through a dedicated daemon process
-.SH "SYNOPSIS"
-.HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\fBcnid_dbd\fR\fB\fR\ 'u
-\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR
-.br
-\fBcnid_dbd\fR\fB\fR \-v | \-V 
-.SH "DESCRIPTION"
-.PP
-\fBcnid_dbd\fR
-provides an interface for storage and retrieval of catalog node IDs (CNIDs) and related information to the
-\fBafpd\fR
-daemon\&. CNIDs are a component of Macintosh based file systems with semantics that map not easily onto Unix file systems\&. This makes separate storage in a database necessary\&.
-\fBcnid_dbd\fR
-is part of the
-\fBCNID backend\fR
-framework of
-\fBafpd\fR
-and implements the
-\fBdbd\fR
-backend\&.
-.PP
-\fBcnid_dbd\fR
-is never started via the command line or system startup scripts but only by the
-\fBcnid_metad\fR
-daemon\&. There is one instance of
-\fBcnid_dbd\fR
-per netatalk volume\&.
-.PP
-\fBcnid_dbd\fR
-uses the
-\fBBerkleley DB\fR
-database library and uses transactionally protected updates\&. The
-\fBdbd\fR
-backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
-.PP
-\fBcnid_dbd\fR
-inherits the effective userid and groupid from
-\fBcnid_metad\fR
-on startup, which is normally caused by
-\fBafpd\fR
-serving a netatalk volume to a client\&. It changes to the
-\fBBerkleley DB\fR
-database home directory
-\fIdbdir\fR
-that is associated with the volume\&. If the userid inherited from
-\fBcnid_metad\fR
-is 0 (root),
-\fBcnid_dbd\fR
-will change userid and groupid to the owner and group of the database home directory\&. Otherwise, it will continue to use the inherited values\&.
-\fBcnid_dbd\fR
-will then attempt to open the database and start serving requests using filedescriptor
-\fIclntfd\fR\&. Subsequent instances of
-\fBafpd\fR
-that want to access the same volume are redirected to the running
-\fBcnid_dbd\fR
-process by
-\fBcnid_metad\fR
-via the filedescriptor
-\fIctrlfd\fR\&.
-.PP
-\fBcnid_dbd\fR
-can be configured to run forever or to exit after a period of inactivity\&. If
-\fBcnid_dbd\fR
-receives a TERM or an INT signal it will exit cleanly after flushing dirty database buffers to disk and closing
-\fBBerkleley DB\fR
-database environments\&. It is safe to terminate
-\fBcnid_dbd\fR
-this way, it will be restarted when necessary\&. Other signals are not handled and will cause an immediate exit, possibly leaving the CNID database in an inconsistent state (no transactions) or losing recent updates during recovery (transactions)\&.
-.PP
-The
-\fBBerkleley DB\fR
-database subsystem will create files named log\&.xxxxxxxxxx in the database home directory
-\fIdbdir\fR, where xxxxxxxxxx is a monotonically increasing integer\&. These files contain ithe transactional database changes\&. They will be removed regularily, unless the
-\fBlogfile_autoremove\fR
-option is specified in the
-\fIdb_param\fR
-configuration file (see below) with a value of 0 (default 1)\&.
-.PP
-Do not use
-\fBcnid_dbd\fR
-for databases on NFS mounted file systems\&. It makes the whole point of securing database changes properly moot\&. Use the dbdir: Option in the appropriate
-\fBAppleVolumes\fR
-configuration file to put the database onto a local disk\&.
-.SH "OPTIONS"
-.PP
-\fB\-v, \-V\fR
-.RS 4
-Show version and exit\&.
-.RE
-.SH "CONFIGURATION"
-.PP
-\fBcnid_dbd\fR
-reads configuration information from the file
-\fIdb_param\fR
-in the database directory
-\fIdbdir\fR
-on startup\&. If the file does not exist or a parameter is not listed, suitable default values are used\&. The format for a single parameter is the parameter name, followed by one or more spaces, followed by the parameter value, followed by a newline\&. The following parameters are currently recognized:
-.PP
-\fBlogfile_autoremove\fR
-.RS 4
-If set to 0, unused Berkeley DB transactional logfiles (log\&.xxxxxxxxxx in the database home directory) are not removed on startup of
-\fBcnid_dbd\fR
-and on a reqular basis\&. Default: 1\&.
-.RE
-.PP
-\fBcachesize\fR
-.RS 4
-Determines the size of the Berkeley DB cache in kilobytes\&. Default: 8192\&. Each
-\fBcnid_dbd\fR
-process grabs that much memory on top of its normal memory footprint\&. It can be used to tune database performance\&. The
-\fBdb_stat\fR
-utility with the
-\fB\-m\fR
-option that comes with Berkely DB can help you determine wether you need to change this value\&. The default is pretty conservative so that a large percentage of requests should be satisfied from the cache directly\&. If memory is not a bottleneck on your system you might want to leave it at that value\&. The
-\fBBerkeley DB Tutorial and Reference Guide\fR
-has a section
-\fBSelecting a cache size\fR
-that gives more detailed information\&.
-.RE
-.PP
-\fBflush_frequency\fR, \fBflush_interval\fR
-.RS 4
-\fIflush_frequency\fR
-(Default: 1000) and
-\fIflush_interval\fR
-(Default: 1800) control how often changes to the database are checkpointed\&. Both of these operations are performed if either i) more than
-\fIflush_frequency\fR
-requests have been received or ii) more than
-\fIflush_interval\fR
-seconds have elapsed since the last save/checkpoint\&. Be careful to check your harddisk configuration for on disk cache settings\&. Many IDE disks just cache writes as the default behaviour, so even flushing database files to disk will not have the desired effect\&.
-.RE
-.PP
-\fBfd_table_size\fR
-.RS 4
-is the maximum number of connections (filedescriptors) that can be open for
-\fBafpd\fR
-client processes in
-\fBcnid_dbd\&.\fR
-Default: 512\&. If this number is exceeded, one of the existing connections is closed and reused\&. The affected
-\fBafpd\fR
-process will transparently reconnect later, which causes slight overhead\&. On the other hand, setting this parameter too high could affect performance in
-\fBcnid_dbd\fR
-since all descriptors have to be checked in a
-\fBselect()\fR
-system call, or worse, you might exceed the per process limit of open file descriptors on your system\&. It is safe to set the value to 1 on volumes where only one
-\fBafpd\fR
-client process is expected to run, e\&.g\&. home directories\&.
-.RE
-.PP
-\fBidle_timeout\fR
-.RS 4
-is the number of seconds of inactivity before an idle
-\fBcnid_dbd\fR
-exits\&. Default: 600\&. Set this to 0 to disable the timeout\&.
-.RE
-.SH "UPDATING"
-.PP
-Note that the first version to appear
-\fIafter\fR
-Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
-.PP
-In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Stop the to be upgraded old version of Netatalk
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Using the old BerkeleyDB utilities run
-\fBdb_recover \-h <path to \&.AppleDB>\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Using the new BerkeleyDB utilities run
-\fBdb_upgrade \-v \-h <path to \&.AppleDB> \-f cnid2\&.db\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Again using the new BerkeleyDB utilities run
-\fBdb_checkpoint \-1 \-h <path to \&.AppleDB>\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-Start the the new version of Netatalk
-.RE
-.SH "SEE ALSO"
-.PP
-\fBcnid_metad\fR(8),
-\fBafpd\fR(8),
-\fBdbd\fR(1)
diff --git a/man/man8/cnid_metad.8.in b/man/man8/cnid_metad.8.in
new file mode 100644 (file)
index 0000000..0a0d559
--- /dev/null
@@ -0,0 +1,90 @@
+'\" t
+.\"     Title: cnid_metad
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 23 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "CNID_METAD" "8" "23 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+cnid_metad \- start cnid_dbd daemons on request
+.SH "SYNOPSIS"
+.HP \w'\fBcnid_metad\fR\fB\fR\fBcnid_metad\fR\fB\fR\ 'u
+\fBcnid_metad\fR\fB\fR [\-d] [\-F\ \fIconfiguration\ file\fR]
+.br
+\fBcnid_metad\fR\fB\fR \-v | \-V 
+.SH "DESCRIPTION"
+.PP
+\fBcnid_metad\fR
+waits for requests from
+\fBafpd\fR
+to start up instances of the
+\fBcnid_dbd\fR
+daemon\&. It keeps track of the status of a
+\fBcnid_dbd\fR
+instance once started and will restart it if necessary\&.
+\fBcnid_metad\fR
+is normally started at boot time by
+\fBnetatalk\fR(8) and runs until shutdown\&.
+.SH "OPTIONS"
+.PP
+\fB\-d\fR
+.RS 4
+\fBcnid_metad will remain in the foreground and\fR
+will also leave the standard input, standard output and standard error file descriptors open\&. Useful for debugging\&.
+.RE
+.PP
+\fB\-F\fR \fIconfiguration file\fR
+.RS 4
+Use
+\fIconfiguration file\fR
+as the configuration file\&. The default is
+\fI@pkgconfdir@/afp\&.conf\fR\&.
+.RE
+.PP
+\fB\-v, \-V\fR
+.RS 4
+Show version and exit\&.
+.RE
+.SH "CAVEATS"
+.PP
+\fBcnid_metad\fR
+does not block or catch any signals apart from SIGPIPE\&. It will therefore exit on most signals received\&. This will also cause all instances of
+\fBcnid_dbd\*(Aqs\fR
+started by that
+\fBcnid_metad\fR
+to exit gracefully\&. Since state about and IPC access to the subprocesses is only maintained in memory by
+\fBcnid_metad\fR
+this is desired behaviour\&. As soon as
+\fBcnid_metad\fR
+is restarted
+\fBafpd\fR
+processes will transparently reconnect\&.
+.SH "SEE ALSO"
+.PP
+\fBnetatalk\fR(8),
+\fBcnid_dbd\fR(8),
+\fBafpd\fR(8),
+\fBdbd\fR(1),
+\fBafp.conf\fR(5)
diff --git a/man/man8/cnid_metad.8.tmpl b/man/man8/cnid_metad.8.tmpl
deleted file mode 100644 (file)
index f96778f..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-'\" t
-.\"     Title: cnid_metad
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "CNID_METAD" "8" "23 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-cnid_metad \- start cnid_dbd daemons on request
-.SH "SYNOPSIS"
-.HP \w'\fBcnid_metad\fR\fB\fR\fBcnid_metad\fR\fB\fR\ 'u
-\fBcnid_metad\fR\fB\fR [\-d] [\-F\ \fIconfiguration\ file\fR]
-.br
-\fBcnid_metad\fR\fB\fR \-v | \-V 
-.SH "DESCRIPTION"
-.PP
-\fBcnid_metad\fR
-waits for requests from
-\fBafpd\fR
-to start up instances of the
-\fBcnid_dbd\fR
-daemon\&. It keeps track of the status of a
-\fBcnid_dbd\fR
-instance once started and will restart it if necessary\&.
-\fBcnid_metad\fR
-is normally started at boot time by
-\fBnetatalk\fR(8) and runs until shutdown\&.
-.SH "OPTIONS"
-.PP
-\fB\-d\fR
-.RS 4
-\fBcnid_metad will remain in the foreground and\fR
-will also leave the standard input, standard output and standard error file descriptors open\&. Useful for debugging\&.
-.RE
-.PP
-\fB\-F\fR \fIconfiguration file\fR
-.RS 4
-Use
-\fIconfiguration file\fR
-as the configuration file\&. The default is
-\fI:ETCDIR:/afp\&.conf\fR\&.
-.RE
-.PP
-\fB\-v, \-V\fR
-.RS 4
-Show version and exit\&.
-.RE
-.SH "CAVEATS"
-.PP
-\fBcnid_metad\fR
-does not block or catch any signals apart from SIGPIPE\&. It will therefore exit on most signals received\&. This will also cause all instances of
-\fBcnid_dbd\'s\fR
-started by that
-\fBcnid_metad\fR
-to exit gracefully\&. Since state about and IPC access to the subprocesses is only maintained in memory by
-\fBcnid_metad\fR
-this is desired behaviour\&. As soon as
-\fBcnid_metad\fR
-is restarted
-\fBafpd\fR
-processes will transparently reconnect\&.
-.SH "SEE ALSO"
-.PP
-\fBnetatalk\fR(8),
-\fBcnid_dbd\fR(8),
-\fBafpd\fR(8),
-\fBdbd\fR(1),
-\fBafp.conf\fR(5)
diff --git a/man/man8/netatalk.8.in b/man/man8/netatalk.8.in
new file mode 100644 (file)
index 0000000..39790ff
--- /dev/null
@@ -0,0 +1,67 @@
+'\" t
+.\"     Title: netatalk
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\"      Date: 22 Mar 2012
+.\"    Manual: @NETATALK_VERSION@
+.\"    Source: @NETATALK_VERSION@
+.\"  Language: English
+.\"
+.TH "NETATALK" "8" "22 Mar 2012" "@NETATALK_VERSION@" "@NETATALK_VERSION@"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+netatalk \- Netatalk AFP server service controller daemon
+.SH "SYNOPSIS"
+.HP \w'\fBnetatalk\fR\fB\fR\fBnetatalk\fR\fB\fR\ 'u
+\fBnetatalk\fR\fB\fR
+.br
+\fBnetatalk\fR\fB\fR
+.SH "DESCRIPTION"
+.PP
+\fBnetatalk\fR
+is the service controller daemon responsible for starting and restarting the AFP daemon
+\fBafpd\fR
+and the CNID daemon
+\fBcnid_metad\fR\&. It is normally started at boot time from /etc/rc\&.
+.SH "SIGNALS"
+.PP
+SIGTERM
+.RS 4
+Stop Netatalk service, AFP and CNID daemons
+.RE
+.PP
+SIGHUP
+.RS 4
+Sending a
+\fBSIGHUP\fR
+will cause the AFP daemon reload its configuration file\&.
+.RE
+.SH "FILES"
+.PP
+@pkgconfdir@/afp\&.conf
+.RS 4
+configuration file used by afpd and cnid_metad
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBcnid_metad\fR(8),
+\fBafp.conf\fR(5)\&.
diff --git a/man/man8/netatalk.8.tmpl b/man/man8/netatalk.8.tmpl
deleted file mode 100644 (file)
index 61cfffe..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-'\" t
-.\"     Title: netatalk
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Mar 2012
-.\"    Manual: Netatalk 3.0
-.\"    Source: Netatalk 3.0
-.\"  Language: English
-.\"
-.TH "NETATALK" "8" "22 Mar 2012" "Netatalk 3.0" "Netatalk 3.0"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-netatalk \- Netatalk AFP server service controller daemon
-.SH "SYNOPSIS"
-.HP \w'\fBnetatalk\fR\fB\fR\fBnetatalk\fR\fB\fR\ 'u
-\fBnetatalk\fR\fB\fR
-.br
-\fBnetatalk\fR\fB\fR
-.SH "DESCRIPTION"
-.PP
-\fBnetatalk\fR
-is the service controller daemon responsible for starting and restarting the AFP daemon
-\fBafpd\fR
-and the CNID daemon
-\fBcnid_metad\fR\&. It is normally started at boot time from /etc/rc\&.
-.SH "SIGNALS"
-.PP
-SIGTERM
-.RS 4
-Stop Netatalk service, AFP and CNID daemons
-.RE
-.PP
-SIGQUIT
-.RS 4
-Restart AFP and CNID master daemons, but keep all session AFP processes running\&. Can be used to implement
-AFP service without downtime\&.
-.RE
-.PP
-SIGHUP
-.RS 4
-Sending a
-\fBSIGHUP\fR
-will cause the AFP daemon reload its configuration file\&.
-.RE
-.SH "FILES"
-.PP
-:ETCDIR:/afp\&.conf
-.RS 4
-configuration file used by afpd and cnid_metad
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafpd\fR(8),
-\fBcnid_metad\fR(8),
-\fBafp.conf\fR(5)\&.
index 033c9e1d3784c3b4c0651100d0243c2aa02bf84f..34e28f484a371874a1c5a2f57e0ed554980d78b9 100644 (file)
@@ -31,7 +31,6 @@ test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
                                $(top_srcdir)/etc/afpd/file.c \
                                $(top_srcdir)/etc/afpd/filedir.c \
                                $(top_srcdir)/etc/afpd/fork.c \
-                               $(top_srcdir)/etc/afpd/gettok.c \
                                $(top_srcdir)/etc/afpd/hash.c \
                                $(top_srcdir)/etc/afpd/mangle.c \
                                $(top_srcdir)/etc/afpd/messages.c \
@@ -65,3 +64,12 @@ test_LDADD = \
        @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@
 
 test_LDFLAGS = -export-dynamic
+
+if WITH_DTRACE
+DTRACE_OBJ = test-afp_dsi.o test-fork.o test-appl.o test-catsearch.o test-directory.o test-enumerate.o test-file.o test-filedir.o
+afp_dtrace.o: $(top_srcdir)/include/atalk/afp_dtrace.d $(DTRACE_OBJ)
+       if test -f afp_dtrace.o ; then rm -f afp_dtrace.o ; fi
+       $(LIBTOOL) --mode=execute dtrace -G -s $(top_srcdir)/include/atalk/afp_dtrace.d -o afp_dtrace.o $(DTRACE_OBJ)
+test_LDADD += afp_dtrace.o @DTRACE_LIBS@
+CLEANFILES += afp_dtrace.o
+endif
index 4387bdfca7e852f7cb45e4f943cd352501c69df5..92347f1cf824da1bec05ae7ef4eacc74ad4bcee1 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index 2a0fcd23c43ec6a6ff12acefe613767e6a4a0a35..933f7f61dc85f570dd5ac69bc33f7c45d04ea0c3 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: afpfunc_helpers.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index ee32144a51682eeaa2d0af8358133274406fce7b..2a190eae0acff3b986392b1ae9f7dc711b510732 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
index 1731ddc141fc4ad1ec27b9b9dc8541be7d4d5bdc..b25ec4dd9c9fd127a470463e0886d6eb18294fff 100644 (file)
@@ -67,7 +67,7 @@ int main(int argc, char **argv)
     TEST_int( afp_config_parse(&obj, NULL), 0);
     TEST_int( configinit(&obj), 0);
     TEST( cnid_init() );
-    TEST( load_volumes(&obj, NULL) );
+    TEST( load_volumes(&obj) );
     TEST_int( dircache_init(8192), 0);
     obj.afp_version = 32;
 
index 382a5bd5bfaaf1502b7626cbdb7192b265f2c05b..98d0c1ba9e7b0691e996e539479f668f247fb450 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify