# 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
+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
-3.0.1
\ No newline at end of file
+3.0.4
\ No newline at end of file
setuplog("default:note", "/dev/tty");
- if (load_volumes(&obj, NULL) != 0)
+ if (load_volumes(&obj) != 0)
return 1;
if (STRCMP(argv[1], ==, "ls"))
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;
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;
return (1);
}
acl_free(acl);
-#endif
return (0);
}
+#endif
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);
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:
}
}
-exit:
closevol(&dvolume);
return rval;
}
}
return 0;
}
-
-static void
-preserve_fd_acls(int source_fd,
- int dest_fd,
- const char *source_path,
- const char *dest_path)
-{
- ;
-}
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);
int ad_set(int argc, char **argv, AFPObj *obj)
{
- int c, firstarg;
+ int c;
afpvol_t vol;
struct stat st;
int adflags = 0;
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));
}
#
# 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.
/*
- * $Id: asingle.c,v 1.14 2010-01-27 21:27:53 didg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $Id: asingle.h,v 1.4 2010-01-27 21:27:53 didg Exp $
*/
#ifndef _ASINGLE_H
/*
- * $Id: hqx.c,v 1.18 2010-01-27 21:27:53 didg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $Id: hqx.h,v 1.3 2010-01-27 21:27:53 didg Exp $
*/
#ifndef _HQX_H
/*
- * $Id: macbin.c,v 1.15 2010-01-27 21:27:53 didg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $Id: macbin.h,v 1.4 2010-01-27 21:27:53 didg Exp $
*/
#ifndef _MACBIN_H
/*
- * $Id: megatron.h,v 1.5 2009-10-14 01:38:28 didg Exp $
*/
#ifndef _MEGATRON_H
/*
- * $Id: nad.c,v 1.18 2010-01-27 21:27:53 didg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $Id: nad.h,v 1.5 2010-01-27 21:27:53 didg Exp $
*/
#ifndef _NAD_H
/*
- * $Id: updcrc.c,v 1.5 2009-10-13 22:55:36 didg Exp $
*
* updcrc(3), crc(1) - calculate crc polynomials
*
/*
- * $Id: updcrc.h,v 1.1 2009-10-13 22:55:36 didg Exp $
*/
#ifndef _UPDCRC_H
"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;
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;
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
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;
+/*
+ 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>
struct flag_map flag_map[] = {
flag(CONV_ESCAPEHEX),
- flag(CONV_ALLOW_COLON),
flag(CONV_UNESCAPEHEX),
flag(CONV_ESCAPEDOTS),
flag(CONV_IGNORE),
{
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);
}
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);
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);
}
#!/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
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
#
--- /dev/null
+# 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
--- /dev/null
+<!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>
Makefile
Makefile.in
netatalk.pam
-.gitignore
+netatalk
*.o
## 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" \
<$< >$@
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
+++ /dev/null
-#%PAM-1.0
-auth @PAM_DIRECTIVE@ @PAM_AUTH@
-account @PAM_DIRECTIVE@ @PAM_ACCOUNT@
-password @PAM_DIRECTIVE@ @PAM_PASSWORD@
-session @PAM_DIRECTIVE@ @PAM_SESSION@
--- /dev/null
+#%PAM-1.0
+auth @PAM_DIRECTIVE@ @PAM_AUTH@
+account @PAM_DIRECTIVE@ @PAM_ACCOUNT@
+password @PAM_DIRECTIVE@ @PAM_PASSWORD@
+session @PAM_DIRECTIVE@ @PAM_SESSION@
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)
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)
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
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
],
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
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:
+
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
--- /dev/null
+#!/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()
use File::Spec;
use File::Temp qw /tempfile/;
use bigint; # require perl >= 5.8
+use IPC::Open2 qw /open2/;
# check command for extended attributes -----------------------------------
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;
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;
}
-e s@:SBINDIR:@${sbindir}@ \
-e s@:ETCDIR:@${pkgconfdir}@ \
-e s@:NETATALK_VERSION:@${NETATALK_VERSION}@ \
+ -e s@:PATH_NETATALK_LOCK:@${PATH_NETATALK_LOCK}@ \
<$< >$@
GENERATED_FILES = \
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
if USE_REDHAT_SYSV
-sysvdir = /etc/rc.d/init.d
+sysvdir = $(INIT_DIR)
sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.redhat
if USE_SYSTEMD
-servicedir = /lib/systemd/system
+servicedir = $(INIT_DIR)
service_DATA = netatalk.service
netatalk.service: service.systemd
if USE_SUSE_SYSV
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.suse
if USE_NETBSD
-sysvdir = /etc/rc.d
+sysvdir = $(INIT_DIR)
sysv_SCRIPTS = netatalk
netatalk: rc.netbsd
if USE_SOLARIS
-servicedir = /lib/svc/manifest/network/
+servicedir = $(INIT_DIR)
service_DATA = netatalk.xml
install-data-hook:
if USE_GENTOO
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.gentoo
if USE_DEBIAN
-sysvdir = /etc/init.d
+sysvdir = $(INIT_DIR)
sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.debian
netatalk_startup() {
# Check that networking is up.
if [ ${NETWORKING} = "no" ]; then
- echo "[Network isn't started]";
+ echo "[Network isn't started]";
exit 1;
fi
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
'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
[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
Makefile.in
Makefile
-.gitignore
-*.o
+html.xsl
+man.xsl
\ No newline at end of file
- 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> </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.
-# 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
--- /dev/null
+%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
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW
du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT
l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj
PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9
Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A
VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ
IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8
Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man
9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd
II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8
X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l
jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM
VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN
ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj
F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk
Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9
/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8
1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd
WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG
D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui
rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+
bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3
Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd
YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8
TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku
xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P
gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA
dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu
w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK
s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma
hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t
dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q
7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz
fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx
aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz
KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV
dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr
G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5
5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu
DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE
dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0
b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG
syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW
8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M
aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz
DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj
irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx
V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF
PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE
v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI
qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2
KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ
vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp
xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C
81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5
Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd
+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/
ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX
UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/
AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k
9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx
Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4
o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa
KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ
/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW
o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf
lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1
Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na
SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc
Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0
4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg
k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr
+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD
6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM
nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn
v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu
/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL
79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB
1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk
rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw
zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf
/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt
iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+
r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb
Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L
dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te
SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1
Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/
AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8
/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o
pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW
+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2
5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s
8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC
ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/
8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p
8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq
wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A
Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu
+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA
8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h
f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7
uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK
t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n
cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2
7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX
2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX
u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z
9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc
8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba
qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69
piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH
/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq
ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK
se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/
1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A
kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd
5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW
H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2
vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd
5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn
/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV
fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5
x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13
y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A
bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/
/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4
NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x
i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM
AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY
q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp
flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L
wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+
RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP
OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV
fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q
8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT
W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE
tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo
6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+
Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A
yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5
OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn
/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6
ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5
f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+
f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7
A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY
UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q
pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp
HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf
9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv
y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380
tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl
djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK
on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr
9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/
HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn
fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV
2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8
66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A
oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3
/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj
8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7
UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm
Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h
wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV
REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/
8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl
zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH
NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/
AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq
2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0
r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr
Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/
AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd
kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN
/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/
AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q
ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl
pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx
+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5
oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl
pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+
kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8
j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM
akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI
rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib
r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr
p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE
v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d
FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN
Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV
dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0
NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a
1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA
6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+
fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8
Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N
j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q
4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF
7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu
IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/
zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK
u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/
AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3
/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE
gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+
g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m
aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN
jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81
a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W
O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ
hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV
vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS
7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj
odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN
B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH
cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK
UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj
/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f
aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9
WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L
Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB
V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G
1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7
+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/
u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq
+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq
eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi
rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+
cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G
Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz
svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35
V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv
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Þ\807¨\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\aKº\92A_1ITíµ\987ÊãU\ f\7f7t\a&F/°ÀMds>PaD\13ra6û+&\98J\ 1\ro9Þ\90È\88\820ñ\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\87Dò\ 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\bu¦
+ºèf (4\ 5*ä\ 2Ë\9b\10Pi\ 23Ò\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\8a9ta\12t\82à¼$\91¡9oþ\ 2\9eó\93ŸÛ\1f\7fñ\93\7f\ 1\ e\92I{Ùd\1a\90¥(/G%¡C¥\7fÓ\9bYö\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\fUú\8c\8fT2åfÜ\8aúÿgà¨ã\17ÆWc\89ÓË\98ä\ efL \18ÿqX ·=\ 6xø\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;ÿ\16pÏ\86\v1\1e¶Æ-Ñ\8bÛU\90Èü\80ü\82=3\9a$©M\ 5\9az\aè7}±\98\87òòc\9da,\8bn\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\96gø\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Ú\93fá\1f&\9dJÓéd\9aJ\93i\82\15Ø\12[d\v,ÏæY\ e\1e³,æX\9aM²\14K²\ 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\ 2U¢\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\8egï\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\1duý\87¥\18\r¯³\98Í'£\7fÖ\92ýûä03k!·\ 5y`:Ü\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¶ÈÜ\fmù\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\16RÀ\129hM\12\ 62®\16ó)\80r&\84I¥\92){è$PÒë¹íÛV\ 5meW\95hJ×k.¶Æ³i\v¤½ó\a\10÷»ÞYÿ¿z.Ù !_ÙU7&Ò0\11uE\1cnAr¼¥n\7fÞj÷\87ýù\1fuÖ\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\9bzµ\86}Em\95éèNûq\99\9br\v0OTMCþÌ\9b_Ì'ªpõ\96Ä\9aóN[S \7fÖ\1f-\86-MàHÛ\891òx\90?«·D\185À\9f\95Fí\1e:ñÙ\1föª½?¦\11\19º¢\9dS`ÙLC¨i\85¡ccÑ\ 6Æ
+\13Pù\eDß
+¸õ¢\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÷\92Eç\r\16]o\17¥ÞÊv
+Þ\1e\97\f¥É"\19¬\98ôR\r\19stt\ 1½\84UÓü\92Q\8dKõ\86Ã\b\96û^ÉëEC¿\82æªô²\ 3[õ¼\92:»f\96Ô}]nIof×
+á3ì>\99¬UBs)\13 ¶Ã\1e\ 6d\10%<d}¿Ù|\18ïJ\10±\f÷d]\ f:\83G¯Éý5¿ÄÍ;Óî\b\1e\ fÇ®\89\9av]\ 3\97\92uê\edZa¿¾ãt*Æûèàa|\b¾¹»\9eó\89²às´sOQ\8bgØ\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\8bfã\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\9e9±\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²\ 2yÏjvÎ'mT§ \9b\e\1d\1f¥ñ\9bظ2S¡«\88ôî6k\81è®\17\85w%)¬Ã\842Ã\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õ¥«^¤áÞÖ\f5Ë\15+EAÝ\ 5\89{ïf\18$ü4\ 2û3Yx§ ªÞÉØÛ\93¦\0£\96À}µúc\94\1dÕ!\8az\ 1\99úê\18Fì\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;\8aS¹\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\ 5Zë\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Çõ¢¥¹\116ù\ 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À\ eXî\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\91N©\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ê\fD÷\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é\8beÉ\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\ 5K÷\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*\9aAè\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á \16O¾\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\97IÖ\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|Ú\ 3UØ\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îç\8eOï\ 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\9eH°\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(ÇåáÅ(\92eË\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Âê\1egGå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\aCé\94xô\0^Qù'\7fEòI\b³O\a¹ãìt_¯\8b\8a\e\ 4¿u\13:.\12³è\13\93½£KùT¬z\8d\f\ 5¹\8c\ 5wÂ\84¾ Ôå\9a´'ÔÆ¥Nj\f\98cñ\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\1dZ¥\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å'\17Oë\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Å\95B¥\9c¾:+¥\94ñë³ö\99þ[ô+\7f[\9c\85uÁ\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û"\8bLÃ\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\96H÷\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\ 3LÒ\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\9cMë\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\1ciå\8e÷\1f\1ec¤ÉÓ»Z\r#EX¤±î?ÏRíç:BJ,1ø\92;Ü{\9c_\9f\ 1RjÉ:\8bùöø.âO\1e[?åÓ`\93\9bwµ\97sËç\8b\96ï\988{\89Ìm\9e\ 6²dkVß·z
+3 D}~þ \88\9eZY\98J\e\ 2Aá\80DÏ\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\8cOÃ\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\ 64¢Å±\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ß)YY\8dÏã×\8f\10ÿh=\16fP\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\18Uè\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%\15IÞ\ 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\9bF°\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\1agÒ\ 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ÑÛÈ\0L«\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ú"É\98s¾\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Î\9diFujm8Û³^\ 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,\88YË\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Ã\19ZÞ\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\8bvç\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\ 2fü\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\9fXñ\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\ ekõ\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\85MÈ\8eíèhÕsÑö`\951\aë"\93T\ f\98\8f ¹\98:«\9bY\9e\8bë\1dr(\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½æw7Ì,\9bÏ\8e\96vñ\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\16n÷\97ò9Þ\8f¼³KùÜÞ_¹Å¥|\86¬\ 2"ªF¿Ø\10uuçª4ØæV¿]|\e
+Ýë·\93J®\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æ¾æ)\13t§\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\94Nµ\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\818Í\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à\7fo¬\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<½Û\8bc°\8amß\ fà`\88H¤è\10þ\138\96\9bÀ\9f\99\bþ\13\7fµéæ\17\1ar\18N\0 ö¢ø·äÁññ\87ð\1ez®\12\9fo¹¯ÜQÍ'\ 2ÝU=¡Qbü¡>\bë\1f\14ümõ\ 1:9¬{tGvÕGqý\83¯\93Oõ\ 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\0vì\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_î\feã\92K³\ fmé\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ì\94fÃ\12Ç\92ìÕ@.ا\16$Bº§}å\ 2á{
+5º\8aÿ°\8e\ 5Ïb²\8dY\0\16\ 6]_1!Txê¾)L\88Ç\fL \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\94dÌ\ 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\12Wº\17ª¡o}©4d³õûñ\86Ú\0hN¬\0ÀXÜ\83x\99nkaêï6FÊílÔÛ¢-\r\1e¿»aôf+g#| \ 3pZø\9d\18vF²õï-\85ª>X8\ 3\90tß\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\17Rµ\ eF/Rð\1c8iJ¡0Jgã\1cNL\8eÊ\94@QILUÔ¸³\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§Ç\\16bÚ\ 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Å!\ eD¬\18\94ÇoÚ\ 15í\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\7fxô\ 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\8dpÖ\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ë\81Tò\92\ f¸ b\82eì!\89\7fW+'RÊÙÔ \1a_âP:".¿x1\96%þª\17\96¦DM\1dayÁÇÂQxHÈÇÂ\1fJ-9s\85\82\ 5\9c\1d\rJ©\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&\ 6zü\81?Îá\97\1fhúÛK{/¼Ï¯\84·\8b^¾ñ\90\14Cx92\1dO' Ò;\82¿I2N°\1c©5\92$KÆi\8aà¼i\96\8bÃ\1f\9c¾) \18SÞ\8eGkbÓ\10vS\0A\83ÅÒd\9caá\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\92Má\ 6
+M\0´\0¡\ 4!õI¥ÓX$\80ëTZ\82\8dç\ 1M[<Ťi©)\89_\ 4Z`\82\19}¯\14\13'\92\f©\87\95JÆY\86dõ\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\fM¤\91²%¢0ka\rÆð\10¸\14VDD+\97\94¸\8bͪ\f\f¿*÷\19\9a_Â\ 3¿ÃK:^\ 59Ò \j\93n¤`pÌ\7fjò\9dÄÞ\82u\936"3&<"ä\rèû\8dtM:|°Þ\9b)R\9a 'ú\13tÖü\12xG\ 69\87Å
+p G\81\94\94Bîd\9cB\ 4\894Ø\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<\13Xª\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\99P©\8d:¨Úë\96\8dFTà,¦\88¤\11U
+¬4\b\8b\11\95Ö¨\87ª¾nÙhD\ 5Ö\v/·\ 6Tà"\82L\99P©\8dz¨êë\96\8d:Tà\13\81YPã<0K\148ö\9c\19³\1c·\181«\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\1aQ©
+£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ñ\14C¤\8cÄÂLB¸C\1a\89Õ\1au\84©o[µí\9eXpXÓ\1cc"\96\82Fä\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\97Yú\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\1ex¾\1d4·\v4\12¡\ 4Xÿé\19\83f¢\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
--- /dev/null
+%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
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FUm8y+cvKnle1F15h1a10uEglPrMqoz06iNCebn2UHFXjHmr/nMz8u9NLxaBY3mvTLXjJQW
du3+zlDS/wDJLFXlHmH/AJzM/M2/LJpFnp+jwn7DLG1zOPm8remf+ReKvP8AV/z5/OLVSTdebNQT
l1FpILMda9LYQ4qxW98z+Zb5uV7q17dMepmuJZD0p+0x7YqlmKuxVM7LzP5lsW5WWrXtqw6GG4lj
PSn7LDtirKtI/Pn84tKINr5s1B+PQXcgvB1r0uRNir0Dy9/zmZ+ZtgVTV7PT9YhH22aNrac/J4m9
Mf8AIvFXq/lX/nMz8u9SKRa/Y3mgzNTlJQXluv8As4gsv/JLFXs/lrzl5U80WpuvL2rWuqQgAv8A
VpVdkr0EiA80PswGKpzirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYL+Y351fl/wCQ
IGGt6gJNRpWLSbWkt21RUVSoEYP80hUYq+X/AMwf+cvfP2vNJa+Wo08t6aagSR0mvHXp8UzDinj8
Cgj+Y4q8O1DUtR1K8kvdRupr28mNZbm4kaWVz4s7lmP0nFVBEd2CopZj0UCpxVM7XyxrVxQi3Man
9qUhPwPxfhiqZQeRbtv7+6RP9RS/6+GKo2PyLZD+8uZG/wBUKv6+WKqy+SdIA3eZvcsv8FxVzeSd
II2eZfcMv8VxVRk8i2R/u7mRf9YK36uOKoOfyLdLX0LpH8A6lP1c8VSy68sa1b1JtzIo/aiIf8B8
X4Yqljo6MVdSrDqpFDiqvp+pajpt5He6ddTWV5CaxXNvI0UqHxV0KsPoOKvcfy+/5y98/aC0dr5l
jTzJpooDJJSG8RenwzKOL+PxqSf5hir6g/Ln86vy/wDP8CjRNQEeo0rLpN1SK7WgqaJUiQD+aMsM
VZ1irsVdirsVdirsVdirsVdirsVdirsVdirsVS/X/MOieXtKn1bW72Kw062XlNczNxUeAHdmPQKN
ydhir5K/Nr/nLzW9WabSfIavpOmmqPq8gH1yUdKxLuIFPju/f4Ttir50uLi4uZ5Li4leaeVi8ssj
F3ZjuWZjUknFUTp+j6hqDf6NESlaGU7IPpOKsnsPJNpHR72Qzt3jT4U+/wC0fwxVP7WytLVONvCk
Q/yQAT8z1OKq+KuxV2KuxV2KuxV2KuxVQurK0uk43EKSj/KAJHyPUYqkF/5JtJKvZSGBu0b/ABJ9
/wBofjirGNQ0fUNPb/SYiErQSjdD9IxVDW9xcW08dxbyvDPEweKWNijqw3DKwoQRir6L/KX/AJy8
1vSWh0nz4r6tpooiavGB9ciHSsq7CdR47P3+I7Yq+tdA8w6J5h0qDVtEvYr/AE65XlDcwtyU+IPd
WHQqdwdjiqYYq7FXYq7FXYq7FXYq7FXYq7FXYqwj81Pzd8q/lxo313V5PWv5w36O0qJh69ww+/hG
D9pyKD3NAVXwr+Zn5sebvzD1c32uXHG1jY/UtMhJFtbr0+FSd2p1dtz8tsVYjbWtxdTLDbxmSVui
rirL9I8m28PGXUCJpeohH2B8/wCb9WKskRERQiKFVdgoFAB8hiq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYqtdEdSjqGVtipFQR8jirG9X8m283KXTyIZephP2D8v5f1YqxC5tbi1maG4jMcq9VbFWXfln+
bHm78vNXF9odxytZGH13TJiTbXC9PiUHZqdHXcfLbFX3V+Vf5u+VfzH0b67pEno38AX9I6VKw9e3
Y/dzjJ+y4FD7GoCrN8VdirsVdirsVdirsVdirsVebfnZ+dmi/lnoqO6LfeYL5W/RmmcqAgbGaYjd
YlP0sdh3KqvgvzV5r17zVrlzrmu3b3mo3TVeRuir+yiL0RF6Ko2GKqWj6Hd6nNSMcIFP7yYjYew8
TirPNN0qz06D0rdKE/bkO7Mfc4qr3FxBbQtNO4jiQVZjirrW4W5to7hAVSVQ6hutDuPwxVVxV2Ku
xV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPUtKs9Rg9K4SpH2JBsyn2OKsD1jQ7vTJqSDnAx/dzAbH2P
gcVVfKvmvXvKuuW2uaFdvZ6jatVJF6Mv7SOvR0boynY4q+9PyT/OzRfzM0V3RFsfMFiq/pPTOVQA
dhNCTu0TH6VOx7FlXpOKuxV2KuxV2KuxV2KsI/N381NG/LjyrJq97Se/m5RaVp3KjXE9PvEaVBdu
w9yAVX58+a/NWueateu9d1y5a61G8flI5+yo/ZRF/ZRBsqjoMVa0DQJtTm5NVLRD+8k8f8lff9WK
s/t7eC3hWGBBHEgoqjFXXNzBawPPO4SJBVmOKsA1rW7jVrkIoKW4akMPuduTe+KvQYYlihjiX7Ma
hR8gKYqvxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVicnmO50vWrm1uazWnqcgP2kD/EOPtv0xVk9t
dW91Cs9u4kifowxV1xbwXELQzoJInFGU4qwDX9Am0ybktXtHP7uTw/yW9/14q35U81a55V16013Q
7lrXUbN+Ubj7LD9pHX9pHGzKeoxV+g35Rfmpo35j+VY9XsqQX8PGLVdO5Va3np95jehKN3HuCAqz
fFXYq7FXYq7FUv8AMOv6V5e0S91vVp1ttOsImmuZm7KvYDuzHZQNydhir87vzY/MzV/zD83XGuXx
aO1WsOmWVfht7YElV225H7TnufamKse0PR5tTuxGKrAlDNJ4DwHucVeh29vDbwJBCoSKMUVRiqoz
KqlmICgVJOwAGKvP/MeuvqNx6cRIs4j8A/mP8x/hiqE0OH1tXtI+o9VWI9lPI/qxV6ZirsVdirsV
dirsVdirsVdirsVdirsVdirsVYH50i4axz/37ErfdVf+NcVQOj61daZPzjPKFj+9hPRh/A++KvQr
G+t762S4t25Rt94PcH3xVfcW8NxA8Eyh4pBRlOKvPNc0ebTLsxmrQPUwyeI8D7jFWQ/lP+Zmr/l5
5ut9csS0lq1IdTsq/DcWxILLvtyH2kPY+1cVfoj5e1/SvMOiWWt6TOtzp1/Es1tMvdW7EdmU7MDu
DscVTDFXYq7FXYq+Ov8AnLz82m1bW18h6TNXTdJcSau6HaW8p8MRp1WAHf8AyzvuoxV862ttNdXE
dvCvKWQ8VGKvSdK02DTrNLeLcjeR+7MepxVGYqxfzlrBijGnQtR5BynI7L2X6e+KsNxVO/J0XPW0
b/faO33jj/xtirP8VdirsVdirsVdirsVdirsVdirsVdirsVdirDfPaUurV/5kYfca/xxVi+KppoG
syaZdhiSbaSgmT2/mHuMVeiRyJIiyIwZHAZWHQg7g4qhdV02DUbN7eXYneN+6sOhxV5tdW01rcSW
8y8ZYzxYYq+iv+cQ/wA2m0nW28h6tNTTdWcyaQ7naK8p8UQr0WcDb/LG27HFX2LirsVdirBfzq/M
aDyB+X+oa2GH6RkH1XSYjQ8ruUHgaHqIwDI3suKvzpuLie5uJbi4kaWeZ2kllc1Znc1ZmJ6kk4qz
DybpHo251CVf3swpCD2Tx/2X6sVZNiqldXEdtbyzyfYiUu30CuKvMLu6lurmW4lNXlYsfp7fRiqj
irJPIyj9JTt3EJH3uv8ATFWbYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWH+fP76z/1X/WuKsVx
V2Ksy8l6oZIn0+U1aIc4Sf5Sdx9BxVlGKsZ85aR61uNQiX97CKTAd08f9j+rFWH29xPbXEVxbyNF
PC6yRSoaMroaqykdCCMVfot+Sv5jQef/AMv9P1ssP0jGPqurRCg43cQHM0HQSAiRfZsVZ1irsVfE
v/OXv5gtr3n6Py1ayV03y2npyAH4XvJgGmbb+ReKb9CG8cVeJaPp7ahqEVtvwJrKR2QbnFXpaIqI
qIOKqAFA6ADYYquxVjvnW7MWmpbqaG4ff/VTc/jTFWD4q7FWReR3A1SVD+1CafMMuKs4xV2KuxV2
KuxV2KuxV2KuxV2KuxV2KuxV2KsH87zh9TjiH+6ohX5sSf1UxVjuKuxVGaPeGz1K3uK0VXAf/VbZ
vwOKvTsVWuiujI45KwIYHoQdjirzTWNPbT9QltjXgDWInuh3XFXtv/OIX5gtoPn6Ty1dSU03zInp
xgn4UvIQWhbf+deSbdSV8MVfbWKpN5y8y2vlfypq3mG6AMOl2stzwJpzZFJSMHxd6KPnir8zdS1C
81LUbrUb2QzXl7NJcXMp6vLKxd2PzZicVZj5F0O8+pSagLeR/rB4ROEYjghoaEDu36sVZR+jtQ/5
Zpf+Ab+mKu/R2of8s0v/AADf0xVhfnew1R7+CNbSYqkXLaNzuzH2/wAnFWOfonVf+WOf/kW/9MVd
+idV/wCWOf8A5Fv/AExVMvLtpqdrrFvI1pOEZvTYmN6UccfDxxV6L+jtQ/5Zpf8AgG/pirv0dqH/
ACzS/wDAN/TFXfo7UP8Alml/4Bv6YqteyvEALwSKCQASjDc9BuMVbawvlBZraUKNySjUA+7FUFdX
UFpEZrhikQFS1CRQ9DsDiqBm8w2MTFSk5K05fuZBTlsteQHWm2KrB5n00mhWZfcxN/DFUVBrGmz/
AGJgCezhk/4mFxVGqrMvJQWU9CNxirfB/wCU/dirVD4Yq7FVryRorO7BVUEsT2AxV5lql4b3UJ7k
9JGPEH+UbL+AxVCYq7FXYq9S0+Uy2FtKeskSMf8AZKDiqIxVjPnaw9S0jvUHxQHhIf8AIbp9zfrx
Vimm6heabqNrqNlIYbyymjuLaUdUliYOjD5MoOKv0y8m+ZbXzR5U0nzDagCHVLWK54A14M6gvGT4
o9VPyxV4x/zmZ5qOm/l3Y6BE/GbXrweotftW9mBK/wDyVaLFXxfb289zcRW9vG0s8zrHFEgLMzsa
KqgbkknFX3V5O8/an5Y8q6V5ftPy+1/0NMtYrcMLaQc2RQHc/B1d6sfniqcf8rj17/y3+v8A/SNJ
/wA0Yq7/AJXHr3/lv9f/AOkaT/mjFUt1H8+vM1pcekn5YeZ7leIb1IrSUrv22jOKoX/oYbzT/wCW
o81/9Ic3/VLFXf8AQw3mn/y1Hmv/AKQ5v+qWKu/6GG80/wDlqPNf/SHN/wBUsVTPT/zv8wXdv6rf
lx5kt2qQYpbSVW2+ce4xVE/8rj17/wAt/r//AEjSf80Yq7/lcevf+W/1/wD6RpP+aMVYD+aX53a1
Ppthb/4A1+1EWqWMoubq0mihd45wwhjZkXlJJ9lAOpxVGebvz58zX3lTWrKT8sPM9pHdWFzC93Na
SrFEskLKZJCYxRUrU+2KsF8z/mxrd5/zj3D5Xk8ja5a2S6Pp1qPMMtvILEpAIQs4kKBfTl4fAeXc
Yq7zz+bGt6jq3mKeXyNrli17FoSyQ3FvIrQCzubp0MoKCguDKVj8SpxViM/n7VG6+V9TX5xP/wA0
4ql8/nTUW6+XdRX5xN/zTiqBfzhqSklNFv4z4hGH8MVWjz5qgNJdIunX3QhvvpiqOtdfg1Bfhglg
k7xTIVb6Ox+jFVs45dTx+eKsV8z3ogAtYj6juKyFeir4V8TirE3NWrSmKrcVdirsVem6MpGkWQJr
+4jP3qDiqNxVQvbVLq0mt26SoV+RI2P0HFXlzoyOyMKMpIYe4xV9pf8AOGfmo6l+Xd9oEr8ptBvD
6a1+zb3gMqf8lVlxV5R/zmZ5hN/+ZtnpCNWHR9PjVk8J7lmlc/TH6eKvKvyyv49M896Pq0tqLxNM
nW9+rs3AM8Hxx1YBqUkCnpir6n/6Gnv/APqXYv8ApKb/AKp4q7/oae//AOpdi/6Sm/6p4q7/AKGn
v/8AqXYv+kpv+qeKpN5g/wCcxtU0p4QvlaGVJg3xG7daFabf3R8cVSn/AKHj1X/qUYP+k1/+qOKu
/wCh49V/6lGD/pNf/qjirv8AoePVf+pRg/6TX/6o4q4f85x6nUV8owU70vX/AOqOKp63/OZMS6eL
79BQmM7BBdNz5Urx4+n1xVIz/wA5x6nU8fKMFO1b16/8mcVY355/5yx1DzXptjZSeW4bQWWo2mpB
1umfkbOUSiOhiWnKlK9sVTTX/wDnM3U9Y0LUtJbyrDCuo2s1o0wvHYoJ42j5U9IVpyrTFWL61/zk
rfap+Usf5dtoMUUMenWmm/pEXLMxFmIwJPT9MD4vR6ctq4q7zP8A85K32vX+r3j6DFbnVk0uNkFw
zen+ipp5lIPpivqfWaHwp3xVIJfzoupP+lUg/wCex/5oxVCS/mzcSf8AStQf89T/AM04qhJPzJnf
/jwUf89D/wA04qhJfPUr/wDHmo/2Z/5pxVCyebJXNRbhT2Ic/wBMVRCedpjbuksHKUD90/Lav+Vt
iqRz6i8zMzLVmNSScVQrNyNcVaxV2KtqpZgqirE0A9zir1WCIRQRxDpGqqP9iKYqqYq7FXnXme1+
r61cACiyESr/ALMVP/DVxV7R/wA4Z+YTYfmbeaQ7Uh1jT5FVPGe2ZZUP0R+pirz/APPnVzqv5xeb
Lonlw1CS0B36WYFsOv8AxhxVKfIsHK7up/5EVP8AgzX/AI0xV9R/kd+V3k3zX5Tu9R1u0ee7iv5L
dHWaSMCNYYXAojAfakOKo784PyX8raH5Mm1jy9ayQXNlLG9zylklDQOfTbZyejMpr4VxVb+Tf5Te
SPM3kqLVNYs5Jr1p5oy6zSxjihAX4UYDFWD/APOU/wCVvlXyz5e0i50C1eCZ5pmnLSyS1RFTYcy1
Kc64qnX5Ff8AOPf5W+bfyq0PzBrmmy3GqXv1r6xKlzPGD6V5NClERwookYGwxVnv/Qp/5Jf9Wef/
AKTLn/qpirxz/nJ38l/y+8h+UtK1Hy1YyWt3dX4t5neeaYGP0ZHpSRmA+JRir2P/AKFP/JL/AKs8
/wD0mXP/AFUxV3/Qp/5Jf9Wef/pMuf8Aqpirv+hT/wAkv+rPP/0mXP8A1UxV8Q+cNOtdN8263p1o
pS0sr+6t7dCSxEcUzIgJO5oq4q9P/wCcVfINh5r/ADIabVbKK+0fR7WS4uba5jWWCSSX9zCjo4ZW
+2ziv8uKvsT/AJVP+Vn/AFJuh/8AcNs/+qeKsH/O78t/y7078p/M99p/lbSLO9t7NngurewtopY2
5L8SOkYZT8jir5K/Irynonm381dD8v65C1xpd79a+sRI7Rk+lZzTJR0IYUeMHY4q+uP+hT/yS/6s
8/8A0mXP/VTFXf8AQp/5Jf8AVnn/AOky5/6qYqkH5gf84y/k/o/kPzJq9hpU0d9p2l3t3aSG7uGC
ywW7yRkqzkGjKNjirwD/AJxs/LnQPPvn+40vX4HuNLtdPmu5I0keIl1kiiT44yD1lrSuKvp7/oU/
8kv+rPP/ANJlz/1UxViv5qf84y/ldo/5deYdX0LTJoNV06yku7eVrmeQL6A9R6o7sp+BW6jFXx5p
8STX9tDIKxySojjpszAHFX3b/wBCn/kl/wBWef8A6TLn/qpir5u/5yd/Ljyn5D826Vp3lq1e1tLq
wFxMjyyTEyetIlayFiPhUYqx38gvKmk+avzY0PRdXga40yb6zJcxKzIf3NrLLGeSEHaRFxV9mf8A
Qv35Xf8AVtl/6SZ/+a8VeQ6J+Wej6p+dWoeWkgcaBpzyyzxB25CFEAVedeW8jqOuKvXX/ID8rEUu
+nyKqglmN1OAAOpJ54q+J/zXj0s+ZJLjSojDpkkkyWUZYsRCkh9OpYkk8WFcVRH5DaudK/OLyndA
8eeoR2hO/S8Btj0/4zYqxXzPetfeZdWvW3a6vbiYnbrJKzdtu+Ksh8ix0srmT+aQL/wK1/42xV9h
f84xf8oFf/8AbVm/6h7fFXqOuaTb6xo19pVx/cX0ElvIfASKVqPcVrirBvyEsriw8htY3K8Lm0v7
uCZPB45OLD7xirD/APnKpFfTfL6OOStJdBgehBSMHFWWf8442P1H8mtAta8hG19xP+S1/cMtfoOK
t/n9+ZOu/l55Gj1/RYLW4vHvYbUx3qSPFwkSRiaRSQty+AftYq+QPzS/P/zj+ZOj2mla5Z6dbW9n
cfWonsY543L8GjoxlmmFKOe2KvXPyw/5yt/MTzV5/wBD8u6hp2kRWWpXIgnkt4blZQpUmqF7l1B2
7qcVfVmKvmf87v8AnJrz55E/MS/8taRYaXPY2sdu8cl3FcPKTNCsjVMdxEvVtvhxV8n6zqlxq2sX
2q3KolxqFxLdTJGCEDzOZGChixpVtqk4q+zP+cNvKX6L/Li61+VALjzBds0b9zbWlYYwf+evqnFX
u7XUC3cdozUnljkljTxSJkVz9BlXFXn3/ORUzw/kr5qdKVNsiGvhJPGh/BsVfCPkLzrqvkjzZY+Z
9Kignv8AT/V9GK6V3hPrQvA3JY3ib7MppRhvir62/wCcd/z/APOP5k+Z9S0rXLPTra3s7I3UT2Mc
8bl/VSOjGWaYcaOe2KvfsVfEPm//AJy0/MbVtN1ry5c6do6WOoQ3WnTSRw3QlEUyNCzKWuWXlxba
qkV7Yqyv/nB7S+ep+a9VI/uYbS1Rtt/WeSRwNv8AilcVfWLyRxqGkYIpIUFiAOTEKo37kmgxVB69
piaroWo6W/2L+1mtWrsKTRsh6f62KvzF01Hj1i1RwVdLiNWU9QQ4BGKv1IxV8a/85tf8p/oX/bKH
/UTLiqT/APOKFh6f5m6ReuPimN0kf+qtnNU/S36sVfcWKsF8geW/qvmrzjr8qUk1HUDb25I/3Tbq
ORB8GkYg/wCrirf51eZv0B+XmpSxtxur9RYW29DyuAQ5HusQdh8sVfDHnqOtlbSfyyFf+CWv/GuK
se8sXrWPmXSb1dmtb23mB26xyq3fbtiqWYqzrySoGkOf5pmJ/wCBUYq+vP8AnGL/AJQK/wD+2rN/
1D2+KvXsVQem6Xbaebv6uOK3dw9060oA8gXn/wAEwLfTirxb/nKf/jn+Xf8AjLc/8RjxVm/5D/8A
kqND/wCjr/qMmxVMvzL/AC20L8w/Lq6BrU91b2aXCXQksnjSXnGrKBWWOZePxn9nFXyR/wA5Hfkd
5T/LOy0KfQbu/uX1OS4ScX0kMgUQrGV4elDDT7ZrWuKsO/IH/wAnL5T/AOY5f+Itir9FMVfBf/OW
H/k7dY/4wWf/AFDR4q8ltLW4u7qG0t0MlxcSLFDGOrO5Cqo+ZOKv028m+XLfy15T0jy/BQx6XaQ2
vNRQO0aAO/zdqsfnirFvLfmAa1+c3m60iblb+WdO06wIrUeveNNcykU/yVjU+64qgf8AnJqcQfkd
5ocjlWO1SnT+8vYUr9HLFX59Yq+iv+cJf+U/13/tlH/qJixV9lYq/LbVv+Oref8AGeT/AImcVfYn
/OFOl+h+Xesaiwo97qjRg+McEEVD1/mkYYq9Q/NXXzo2neXzy4Lf+YtIs5D0HCS7V2r7UjrirNcV
fm9590j9Efm9remgcY7fW5hEN/7trktH1/yCMVfpDir46/5zQt5Ln8x/L0EYq8umKi/M3MoxVU/5
x8to7b8zvL1vGKJEtyq/RZzYq+wcVaREQURQoJLEDbdjUn6Sa4q+df8AnKHWbl9b0jReLLbW9u13
y/Zd5nMf/CCL8cVfOHnZQdIQ/wAsykf8CwxVguKuxVnXklgdIcfyzMD/AMCpxV9ef84xf8oFf/8A
bVm/6h7fFXqdxqcFvqNnYybSXqymE16tCFYr/wACSfoxVF4q8K/5yn/45/l3/jLc/wDEY8VZv+Q/
/kqND/6Ov+oybFV/51fmLe/l75Gl8x2dnHfTx3EMAgmZlQiUkE1XfamKvjX84/z21b8z7bS4L/S4
NOGlvM8Zgd3LmYIDXn4eniqA/IH/AMnL5T/5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/wDUNHiqG/5x
i8pf4j/N/STInO00blqtx7G3p6P/ACXePFX37irzv8qPy01jyhqvmzVtY1GLUL7zPf8A152hVkWM
AyME+Lw9Uj5Yqln/ADlNLGn5F+Y1Y0aVrFEG+5F/A1PuU4q+AsVfRX/OEv8Ayn+u/wDbKP8A1ExY
q+ysVfltq3/HVvP+M8n/ABM4q+8/+cXdLNh+Segll4yXhubpxSn95cyBDv4xquKrf+chfKnnbzLp
flmDyrp/1+XTdag1O6AmggKLbI4U1meOu8h+zir1jFXwp/zkvpB0/wDP+6lA4x6ibC7QD3jSJj9L
wscVfdeKvj3/AJzLvJLL8y/Lt1GAXi0sEA9CDcSgj7jirv8AnHq7hvPzN8v3MJrHILojxB+pzVB+
RxV9hYqpW9zDcIzxNyCO8TezRsUYfeMVeM/85PeX/rGgaZrsa1ewna3mI/33cCoJ9leMD/ZYq+UP
OzAaQg/mmUD/AIFjirBcVTPzPZNY+ZdWsm2a1vbiEjbrHKy9tu2Ksh8iyVsrmP8AlkDf8EtP+NcV
fYX/ADjF/wAoFf8A/bVm/wCoe3xVH/nTr58v6l5N1jlxjtdTYzkdfRePhMPpjZsVengggEGoPQ4q
8K/5yn/45/l7/jLc/wDEY8VZh/zj1dx3f5QaFPF/ds16qnxCX861+njiq/8APb8vNa8/+QJvLujT
W1veyXME4kvGkSLjExLCsaStXw+HFXyL+Y//ADjZ558geWX8xazfaZcWUcscBjs5bh5eUpoppJBE
tPH4sVSf8gf/ACcvlP8A5jl/4i2Kv0UxV8F/85Yf+Tt1j/jBZ/8AUNHir2H/AJwp8pfVfLOt+aZo
6S6ncLZWjEb+jajk7L7PJLQ/6mKvd/PHm2w8oeU9T8yX6NJa6ZCZWiUgM7EhURSdqu7BRiqXfld+
Ydr+YHlKHzJaWUthbzSywpBOQzH0m4lgV2IJxVhn/OWJA/JLVwTQmezA9z9ZQ4q+C8VfRX/OEv8A
yn+u/wDbKP8A1ExYq+ysVfltq3/HVvP+M8n/ABM4q/SH8rNLGl/lr5WsOPFoNKsxKKU/eNCrSfe5
OKonzh5+8oeTbSC78zalHptvdSGK3eRXfm4XkQBGrnYDwxVPIZopoUmiYPFKoeNx0KsKgj6MVfJn
/OZGkel+YHk7V6UF5bm0r4m1uRJ/2NYq+tsVeA/85EfkB5x/MnzPpuq6HeadbW9nZC1lS+knjcv6
ryVURQzDjRx3xV5h+R3lvVvI3/ORVr5I1iaCe9sxNIZLVneEtLprz0RpEib7EgrVRuDir7NxVhP5
f639Z8w+ctIdqvp+qeqg8IrmMUH/AAcbH6cVTb8wPL/+IPJesaQF5y3Ns5t1/wCLo/3kX/JRFxV+
f/np6WVtEerSlqf6qkf8bYqx7yxZNfeZdJsl3a6vbeEDbrJKq99u+Ksq/PnSDpX5xebLUjjz1CS7
A36XgFyOv/GbFUp8iz8bu6g/nRX/AOANP+N8VfVH5E/mX5J8r+UbvT9d1L6ndy6hJcRxejPLWNoY
UDcoo3X7SNtXFUJ+ff5ieTvNWkaVb6DqH1ya2uHkmX0Z4uKslAayogO/hirNvJH56+Q4vKOlQa5q
pt9Wgt0hu4jb3LnlF8AblHG6Hmqhtj3xV5j/AM5P/mZ5S8weXNO/w/qH1ya3adZR6U0XH1giqayp
HXoemKpp+Qn59/lP5V/KfQtB17XfqerWf1r6zbfVbyXj6t5NKnxxQuhqjqdmxVn/AP0NH+RP/Uzf
9OOof9k+KvMP+cjfzw/K7zh+Wk+i+XNa+vam93byrb/VbuGqRsSx5zQxpt88VfP/AOUGv6T5f/Mv
y9rWrz/VtMsbtZbq44PJwQKRXhGruevYYq+z/wDoaP8AIn/qZv8Apx1D/snxV8kf85CebvL3m380
tS1zy/d/XdLuIrZIrj05YqmOBEccJljcUYEbjFXoH5ef85aWnkryXpXli08nfWI9Nh4PcfpH0/Vl
djJLJw+qvx5yOzU5GnjiqWfm9/zlHc/mF5Nk8sw+X/0PHPPFLcXH136zzjhJcR8PQhpVwrV5dumK
on8q/wDnKlPIPkbT/Ky+Vf0gbJp2e9+v+h6hmneWvp/V5ePEOF+12riqG/N7/nJ//lYnk2Ty3/hr
9F+pPFP9b+vfWKekSePp/V4etevLFXhWKvaP+cXPzC8n+R/N+rah5o1D9H2dzp5t4JfRnn5SetG/
HjAkrD4VO5FMVfS//Q0f5E/9TN/046h/2T4q+C7t4LjVZn9TjbzTsfVoTRGcnlx69N6Yq+8YP+cn
fyFghjhj8y8Y4lCIPqOobKooP+PfFXgn/OVX5ueTfPSeXLTypqP6QtrE3Ut63o3EAEknpLEKTxxV
2V+leuKvZ/J//OTf5NW3lHRLbU/MXoalBp9rHewmzvmKTpCqyryjgKGjgiqmnhirzH/nJX82Pyu8
66X5ck8uayL6/wBL1HnNF9Wu4SttKlZHrNDGpo0SbA19sVey/wDQ0f5E/wDUzf8ATjqH/ZPirv8A
oaP8if8AqZv+nHUP+yfFXgH/ACtHyJ/0Nj/jz9J/86p/1cvQuP8Aqz/Vf7n0/W/vvh+x79N8VfS3
/K+fyo/6vn/Tref9UcVeXeUfzQ8saX+b/mPWLi94+XtXVxHdelMaupRo29MIZOzD7PfFXqP/ACvj
8qP+r5/063n/AFRxV8Vfnbc6LL51u10ScXGlNLLcWkgR4xwnfkF4uFYcKcdx2xVT/IbSDqv5xeU7
UDlw1CO7I36WYNyen/GHFXoH/OZnl42H5m2erotIdY0+NmfxntmaJx9Efp4q8X8sXX1fWrck0WQm
Jv8AZig/4amKvRcVdiraippiqVeZ9NF3awQGUoC5c0Fa8RTx/wArFUhTybE3/H0w/wBgP64qiE8h
wt/x+MP9gP8AmrFUQn5cwt/x/MP+eY/5qxVER/lfA3/SwYf88h/zViqJj/KS3f8A6WTj/nkP+asV
REf5NWzf9LVx/wA8R/zXiqKsvyMtbi/trU6u6idbpi/oA0+rWU92Nuf7Rt+P01xVkXl7/nGOx1a/
8n2reYJYR5o0FtckcWyt6DKts3ogeoOY/wBK+1t06Yqm1j/ziPp1z581XyufMsyx6bYWd8t19UUl
zdyTIUKertx9DrXvirtf/wCcR9O0rzR5X0RfMs0q+Yri7gec2iqYRa2cl0CF9U8uRi49RirIv+hH
NK/6m6f/AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/
AKQk/wCq2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq
2Ku/6Ec0r/qbp/8ApCT/AKrYq7/oRzSv+pun/wCkJP8Aqtirv+hHNK/6m6f/AKQk/wCq2Ku/6Ec0
r/qbp/8ApCT/AKrYqyeL/nFWwSJEPmOZiqhS31Vd6Clf73FV/wD0Kxp//UxS/wDSMv8A1UxV3/Qr
Gn/9TFL/ANIy/wDVTFXyZ+alhZ6Z+YGtaTZ3RvLfTLg2S3DKELPAOEvwgtSkoYdcVeo/84Z+Xjf/
AJm3mrutYdH0+RlfwnuWWJB9MfqYq9X/AOczPKp1L8u7HX4k5TaDeD1Gp9m3vAIn/wCSqxYq+LUd
kdXU0ZSCp9xir1GyukurSG4XpKgb5EjcfQcVV8VbUVOKpP5k0xbqW3Bmkj4K32DTqR/TFUtj8sxN
/wAflwP9kP6Yqio/KMLf8f1yPk4/piqKj8kwt/0sbsfJx/TFUVH5Cgb/AKWd4Pk4/piqLi/Lm3b/
AKW18PlIP6Yqio/yxtm/6XGoD5SD+mKo3T/yntJtTtLc63qSCZbwl1lAYehp9zcCm37Rh4t/kk4q
ynyt+RFjqOp+RLdvMusQDXvLT6tI8U6hrdglofQg+H4Yv3/T2GKp5p3/ADjlp0/5la1oJ82a6kdl
pljdreLcKJ3NxLcIUduO6J6NVHucVd5n/wCcctO0/wA5eTdKXzZrsy61c3sT3EtwplgEFjLcBoTx
+EsY+Lf5JxVlX/QqWlf9Tt5k/wCkpP8AmjFXf9CpaV/1O3mT/pKT/mjFXf8AQqWlf9Tt5k/6Sk/5
oxV3/QqWlf8AU7eZP+kpP+aMVd/0KlpX/U7eZP8ApKT/AJoxV3/QqWlf9Tt5k/6Sk/5oxV3/AEKl
pX/U7eZP+kpP+aMVd/0KlpX/AFO3mT/pKT/mjFXf9CpaV/1O3mT/AKSk/wCaMVd/0KlpX/U7eZP+
kpP+aMVd/wBCpaV/1O3mT/pKT/mjFU6tf+ceNPt7eOAeatccRqFDtcKSadz8OKqn/Qv9h/1NGtf8
j1/5pxVJ/OX5SaL5Y8qat5guvNGs+jplrLccTcL8TIp4J9nq70UfPFXw5NLJNK80rF5ZGLu7GpLM
akk+5xV9o/8AOGflU6b+Xd9r8qcZtevD6bU+1b2YMSf8lWlxV7P5y8tWvmjypq3l66IEOqWsttzI
rwZ1ISQDxR6MPlir8zdS0+803UbrTr2Mw3llNJb3MR6pLExR1PyZSMVZX5Jv/UtJLJz8UB5xj/Ib
r9zfrxVk2KuxViHnxP3lm9Ooda/Iqf44qxTFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXqOmxmPTrWM9UhjU/QoGKonFWM+dr/ANO0jskPxTnnIP8AIXp97fqxVimm6fealqNr
p1lGZry9mjt7aIdXllYIij5swGKv0y8m+WrXyv5U0ny9akGHS7WK25gU5sigPIR4u9WPzxVOcVfE
v/OXv5fNoPn6PzLax003zInqSED4UvIQFmXb+deL79SW8MVeJaPqDafqEVyK8AaSgd0OzYq9LR1d
FdDyVgCpHQg7jFV2KsW87TWclrFGJUNzFJX0wasFIINadN6Yqw7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYqrWkBuLqGAdZXVB/sjTFXqYAAAGwHQYq07qiM7niqgliegA3OKvNN
Y1BtQ1CW534E0iB7INhir23/AJxC/L5te8/SeZbqOum+W09SMkfC95MCsK7/AMi8n26EL44q+2sV
dirBfzq/LmDz/wDl/qGiBR+kYx9a0mU0HG7iB4Cp6CQExt7Nir86bi3ntriW3uI2inhdo5YnFGV0
NGVgehBGKsw8nawJbZrGdqPAOUTE9Y+4/wBj+rFUDr/muWZ2trBikI2ecbM/+qewxVjRJJqeuKtY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FV8UskUiyRsUkU1VlNCD88VTe283a
1DQNKsyjtIoP4rxOKq2p+bp73TmtRD6LyGkjq1QU8BttXFUjt7ee5uIre3jaWeZ1jiiQVZnc0VVA
6kk4q/Rb8lfy5g8gfl/p+iFR+kZB9a1aUUPK7lA5io6iMARr7LirOsVdirsVfHX/ADl5+UraTra+
fNJhppurOI9XRBtFeU+GU06LOBv/AJY33YYq+cVZlNVJBoRUbbEUOKtYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+j/APnEP8pW1bW28+atDXTdJcx6QjjaW8p8
Uor1WAHb/LO26nFX2LirsVdirsVS/wAw6BpXmHRL3RNWgW506/iaG5hburdwezKd1I3B3GKvzu/N
j8s9X/LzzdcaHfBpLVqzaZe0+G4tiSFbbbkPsuOx9qYqwzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYqzP8p/yz1f8AMPzdb6HYho7VaTane0+G3tgQGbfbkfsoO59q
4q/RHy9oGleXtEstE0mBbbTrCJYbaFeyr3J7sx3Yncnc4qmGKuxV2KuxV2KsI/N38q9G/MfyrJpF
7SC/h5S6VqPGrW89PvMb0Ade49wCFX58+a/KuueVdeu9C1y2a11GzfjIh+yw/ZdG/aRxurDqMVSu
IRGRRKSIyfiKipA8QDirKbfyZZ3EKzQX5kicVVgg/wCasVVP8Bw/8tjf8AP+asVd/gOH/lsb/gB/
zVirv8Bw/wDLY3/AD/mrFXf4Dh/5bG/4Af8ANWKu/wABw/8ALY3/AAA/5qxV3+A4f+Wxv+AH/NWK
u/wHD/y2N/wA/wCasVd/gOH/AJbG/wCAH/NWKu/wHD/y2N/wA/5qxV3+A4f+Wxv+AH/NWKu/wHD/
AMtjf8AP+asVd/gOH/lsb/gB/wA1Yq7/AAHD/wAtjf8AAD/mrFXf4Dh/5bG/4Af81Yq7/AcP/LY3
/AD/AJqxV3+A4f8Alsb/AIAf81Yq7/AcP/LY3/AD/mrFXf4Dh/5bG/4Af81Yqp3Hkyzt4WmnvzHE
gqzFB/zVirFpREJGERJjB+EsKEjxIGKpp5U8q655q1600LQ7ZrrUbx+MaD7Kj9p3b9lEG7MegxV+
g35RflXo35ceVY9IsqT383GXVdR40a4np94jSpCL2HuSSqzfFXYq7FXYq7FXYq7FXm352fknov5m
aKiO62PmCxVv0ZqfGoAO5hmA3aJj9Kncdwyr4L81eVNe8q65c6Hrto9nqNq1Hjboy/sujdHRuqsN
jiqlo+uXemTVjPOBj+8hJ2PuPA4qzzTdVs9Rg9W3epH24zsyn3GKozFXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYqg9S1Wz06D1bh6E/YjG7MfYYqwPWNcu9TmrIeECn93CDsPc+JxVV8q+VNe81
a5baHoVo95qN01EjXoq/tO7dERerMdhir70/JP8AJPRfyz0V0R1vvMF8q/pPU+NAQNxDCDusSn6W
O57BVXpOKuxV2KuxV2KuxV2KuxV2KsI/NT8ovKv5j6N9S1eP0b+AN+jtViUevbsfu5xk/aQmh9jQ
hV8K/mZ+U/m78vNXNjrlvytZGP1LU4QTbXC9fhYjZqdUbcfLfFWI211cWsyzW8hjlXoy4qy/SPOV
vNxi1ACGXoJh9g/P+X9WKskR0dQ6MGVtwwNQR8xiq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtd0RS
7sFVdyxNAB8zirG9X85W8PKLTwJpehmP2B8v5v1YqxC5uri6maa4kMkrdWbFWXfln+U/m78w9XFj
odvxtY2H13U5gRbW69fiYDdqdEXc/LfFX3V+Vf5ReVfy40b6lpEfrX84X9I6rKo9e4YffwjB+ygN
B7mpKrN8VdirsVdirsVdirsVdirsVdirsVS/X/L2ieYdKn0nW7KK/wBOuV4zW0y8lPgR3Vh1DDcH
cYq+Svza/wCcQ9b0lptW8hs+raaKu+kSEfXIh1pE2wnUeGz9viO+KvnS4t7i2nkt7iJ4Z4mKSxSK
UdWGxVlNCCMVROn6xqGntW2lIStTEd0P+xOKsnsPO1pJRL2MwN3kT4k+77Q/HFU/tb20uk5W8ySj
/JIJHzHUYqr4q7FXYq7FXYq7FXYq7FVC6vbS1TlcTJEP8ogE/IdTiqQX/na0jqllGZ27SP8ACn3f
aP4YqxjUNY1DUGrcykpWoiGyD/YjFUNb29xczx29vE808rBIoo1LuzHYKqipJOKvov8AKX/nEPW9
WaHVvPjPpOmmjppEZH1yUdaStuIFPhu/b4Tvir610Dy9onl7SoNJ0SyisNOtl4w20K8VHiT3Zj1L
Hcnc4qmGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVgv5jfkr+X/AJ/gY63p4j1GlItWtaRXa0FB
V6ESAfyyBhir5f8AzB/5xC8/aC0l15akTzJpoqRHHSG8RevxQseL+HwMSf5Rirw7UNN1HTbySy1G
1msryE0ltriNopUPgyOFYfSMVUEd0YMjFWHRgaHFUztfM+tW9ALgyKP2ZQH/ABPxfjiqZQeertf7
+1R/9Rin6+eKo2Pz1ZH+8tpF/wBUq36+OKqy+dtII3SZfYqv8GxVzedtIA2SZvYKv8WxVRk89WQ/
u7aRv9Yqv6uWKoKfz1dt/cWqJ/rsX/VwxVLbrzPrVxUG4Man9mIBPxHxfjiqWO7uxZ2LMerE1OKq
+n6bqOpXkdlp1rNe3kxpFbW8bSyufBUQMx+gYq9x/L7/AJxC8/a80d15lkTy3ppoTHJSa8devwwq
eKeHxsCP5Tir6g/Ln8lfy/8AIECnRNPEmo0pLq11SW7aooaPQCMH+WMKMVZ1irsVdirsVdirsVdi
rsVdirsVdirsVdirsVdirsVdirsVSbzL5N8qeaLUWvmHSbXVIQCE+sxK7JXqY3I5ofdSMVeMeav+
cM/y71IvLoF9eaDM1eMdReW6/wCwlKy/8lcVeUeYf+cM/wAzbAs+kXmn6xCPsKsjW05+aSr6Y/5G
Yq8/1f8AIb84tKJF15T1B+PU2kYvB1p1tjNirFb3yx5lsW43uk3tqw6ia3ljPSv7SjtiqWYq7FUz
svLHmW+bjZaTe3THoIbeWQ9K/sqe2Ksq0j8hvzi1UgWvlPUE5dDdxizHWnW5MOKvQPL3/OGf5m35
V9XvNP0eE/bVpGuZx8kiX0z/AMjMVer+Vf8AnDP8u9NKS6/fXmvTLTlHUWdu3+wiLS/8lcVez+Wv
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\94WË\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%'\bXÙ\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\82OÈ\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\9aOÐ\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\ 5aâ\ f\ ed;@a5gWºÇ¸Eò\85Ø\ 3\rÇë5\10\fTÎé^v\11Y\10Í\ 2\8aTÑ\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Þ\ei\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\ ec¼\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\vnû\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\ 1EÄ\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\bu¦
+ºèf (4\ 5*ä\ 2Ë\9b\10Pi\ 23Ò\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
--- /dev/null
+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;
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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. %t"/>
+ </l:context>
+ <l:context name="title-numbered">
+ <l:template name="example" text="Example. %t"/>
+ </l:context>
+</l:l10n>
+</l:i18n>
+</xsl:stylesheet>
--- /dev/null
+SUBDIRS = man1 man5 man8
\ No newline at end of file
--- /dev/null
+*\.1
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+<?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><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.</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
+ <netatalk-devel@lists.sourceforge.net>.</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>
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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 & 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 & 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
+ <netatalk-devel@lists.sourceforge.net>.</para>
+ </refsect1>
+</refentry>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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 ".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.</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'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>', 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
+ ".bin" 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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+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
--- /dev/null
+<?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 <= 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 = <text> <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 (>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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+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
--- /dev/null
+<?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>
--- /dev/null
+<?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
+ <path to .AppleDB></command></para>
+ </listitem>
+
+ <listitem>
+ <para>Using the new BerkeleyDB utilities run <command>db_upgrade -v -h
+ <path to .AppleDB> -f cnid2.db</command></para>
+ </listitem>
+
+ <listitem>
+ <para>Again using the new BerkeleyDB utilities run
+ <command>db_checkpoint -1 -h <path to .AppleDB></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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+manual.xml
--- /dev/null
+EXTRA_DIST = \
+ configuration.xml \
+ install.xml \
+ intro.xml \
+ upgrade.xml
+
+DISTCLEANFILES = manual.xml
--- /dev/null
+<?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 <-> 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 (>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 < 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 -> uams_pam.so
+uams_dhx.so -> uams_dhx_pam.so
+uams_dhx2.so -> 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>
--- /dev/null
+<?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>$></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>$></prompt> <userinput>mkdir /path/to/new/source/dir</userinput>
+<prompt>$></prompt> <userinput>cd /path/to/new/source/dir</userinput></screen>
+ </listitem>
+
+ <listitem>
+ <para>Now get the source:</para>
+
+ <screen><prompt>$></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>$></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>$></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>$> <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>$></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>$></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>$></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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<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>
--- /dev/null
+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
--- /dev/null
+#
+# 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= 
+zwsp=​
+wj=⁠
+deg=°
+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=‘
+rsquo=’
+ldquo=“
+rdquo=”
+
+[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]
+&=&
+<=<
+>=>
+
+[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 ©)
+(?<!\\)\(C\)=©
+\\\(C\)=(C)
+
+# (R) registered trade mark (entity reference ®
+(?<!\\)\(R\)=®
+\\\(R\)=(R)
+
+# (TM) Trademark (entity reference ™)
+(?<!\\)\(TM\)=™
+\\\(TM\)=(TM)
+
+# -- Spaced and unspaced em dashes (entity reference —).
+# Space on both sides is translated to thin space characters.
+(^-- )=— 
+(\n-- )|( -- )|( --\n)= — 
+(\w)--(\w)=\1—\2
+\\--(?!-)=--
+
+# Replace vertical typewriter apostrophe with punctuation apostrophe.
+(\w)'(\w)=\1’\2
+(\w)\\'(\w)=\1'\2
+
+# ... Ellipsis (entity reference …)
+(?<!\\)\.\.\.=…
+\\\.\.\.=...
+
+# Arrows from the Arrows block of Unicode.
+# -> right arrow
+(?<!\\)->=→
+\\->=->
+# => right double arrow
+(?<!\\)\=>=⇒
+\\\=>==>
+# <- left arrow
+(?<!\\)<-=←
+\\<-=<-
+# <= left double arrow
+(?<!\\)<\==⇐
+\\<\==<=
+
+# Arbitrary entity references.
+(?<!\\)&([:_#a-zA-Z][:_.\-\w]*?;)=&\1
+\\(&[:_#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)[\\]?<(?P<name>http|https|ftp|file|irc):(?P<target>//[^\s<>]*[\w/])>=
+
+# 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)[\\]?<<(?P<attrlist>[\w"_:].*?)>>=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
+[\\]?<(?P<index>\d+)>=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.
+#--------------------------------------------------------------------
--- /dev/null
+#!/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)
--- /dev/null
+#!/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
--- /dev/null
+#
+# 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><{index}></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>}
+— {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>}
+— {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"><<a href="mailto:{email}">{email}</a>></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[]
+
--- /dev/null
+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;
+}
+
+}
--- /dev/null
+#
+# 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[]
--- /dev/null
+#\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
+ <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&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
--- /dev/null
+/* 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;
+}
# 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
file.c \
filedir.c \
fork.c \
- gettok.c \
hash.c \
main.c \
mangle.c \
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@
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\" \
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
*
* @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;
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);
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);
EC_EXIT;
}
+#if 0
/*!
* Add entries of one acl to another acl
*
EC_CLEANUP:
EC_EXIT;
}
+#endif
/*!
* Map Darwin ACE rights to POSIX 1e perm
}
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) {
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.
*/
int ret;
uint32_t allowed_rights = 0;
char *username = NULL;
- uuidtype_t uuidtype;
struct stat st;
bstring parent = NULL;
int is_dir;
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 */
}
#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, ".");
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));
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));
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;
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;
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;
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
#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);
}
}
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;
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
/* 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);
}
}
}
-/* 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_)
{
}
/* 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);
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)
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);
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 */
}
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 */
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));
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));
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++) {
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]);
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
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
#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".
#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
}
/*
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");
/*
- * $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.
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
--- /dev/null
+/* 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"
+};
+
/*
- * $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.
*/
/*
- * $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.
}
/* 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;
#include <atalk/server_ipc.h>
#include <atalk/uuid.h>
#include <atalk/globals.h>
+#include <atalk/unix.h>
#include "auth.h"
#include "uam_auth.h"
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
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);
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 */
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);
}
}
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';
}
#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>
*
*/
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 ? */
};
save_cidx = -1;
while (dsidx > 0) {
dsidx--;
- free(dstack[dsidx].path);
}
}
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... */
/* 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;
}
}
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;
}
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);
int num_rounds = NUM_ROUNDS;
int cwd = -1;
int error;
-
+ int unlen;
+
if (*pos != 0 && *pos != cur_pos) {
result = AFPERR_CATCHNG;
goto catsearch_end;
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:
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)++;
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:
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,
result = AFPERR_MISC;
goto catsearch_end;
}
- } else {
+ break;
+ case S_IFREG:
path.d_dir = currentdir;
+ break;
+ default:
+ continue;
}
ccr = crit_check(vol, &path);
} /* 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. */
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;
}
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)
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:
#include <atalk/netatalk_conf.h>
#include <atalk/unix.h>
#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include <atalk/errchk.h>
#include "volume.h"
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 ) {
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 ) {
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) );
}
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);
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();
}
isadir = path_isadir(path);
- if (isadir || !(of = of_findname(path))) {
+ if (isadir || !(of = of_findname(vol, path))) {
ad_init(&ad, vol);
adp = &ad;
} else
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
}
isadir = path_isadir(path);
- if (isadir || !(of = of_findname(path))) {
+ if (isadir || !(of = of_findname(vol, path))) {
ad_init(&ad, vol);
adp = &ad;
} else
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);
}
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);
int dircache_add(const struct vol *vol,
struct dir *dir)
{
- struct dir *cdir = NULL;
struct dir key;
hnode_t *hn;
}
/* ------------------- */
-static int deletedir(int dirfd, char *dir)
+static int deletedir(const struct vol *vol, int dirfd, char *dir)
{
char path[MAXPATHLEN + 1];
DIR *dp;
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);
}
}
/* 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;
}
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;
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 {
}
/* 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);
}
*
* @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;
/* 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));
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. */
/* 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;
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;
cnid_t id;
struct adouble ad;
struct adouble *adp = NULL;
- bstring fullpath;
+ bstring fullpath = NULL;
AFP_ASSERT(vol);
AFP_ASSERT(dir);
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);
}
* 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
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) {
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);
return err;
}
- if (of_stat(s_path) < 0) {
+ if (of_stat(vol, s_path) < 0) {
return AFPERR_MISC;
}
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 );
* 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,
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 :
/* 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;
}
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",
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 ) {
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);
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);
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
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));
+ }
}
}
}
} 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" */
/*
- $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
/*
* 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) |
(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",
};
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
int rv;
struct addrinfo hints, *servinfo, *p;
- if (udp_initialized == FCE_TRUE)
+ if (udp_initialized == true)
return;
memset(&hints, 0, sizeof hints);
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;
}
/* 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;
}
}
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;
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++)
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;
/* 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;
* 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++)
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;
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);
}
/* 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 );
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;
}
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);
* 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;
}
/* 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;
/*
* 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)
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);
}
}
}
int main( int argc, char*argv[] )
{
- int c,ret;
+ int c;
char *port = FCE_DEFAULT_PORT_STRING;
char *host = "localhost";
if (end_time && now >= end_time)
break;
- register_fce( path, 0, event_code );
+ fce_register(event_code, path, NULL, 0);
ev_cnt++;
#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
#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;
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 {
#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();
// 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];
}
}
-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 */
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 */
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;
}
/* 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;
/* 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;
/* 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;
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;
}
}
- if (islink){
+ if (islink && !vol_syml_opt(vol)) {
uint16_t linkflag;
memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
linkflag |= htons(FINDERINFO_ISALIAS);
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;
}
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) {
|| (bconchar(fullpath, '/') != BSTR_OK)
|| (bcatcstr(fullpath, upath)) != BSTR_OK) {
LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+ bdestroy(fullpath);
return AFPERR_MISC;
}
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
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 */
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);
}
}
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 );
}
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
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 );
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:");
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 :
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;
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 :
* 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;
/* 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 );
}
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;
}
* 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,
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;
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) {
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
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();
+ }
}
}
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;
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;
}
}
/* FIXME use of_statdir ? */
- if (lstat(name, &st)) {
+ if (ostat(name, &st, vol_syml_opt(vol))) {
return -1;
}
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 */
}
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) {
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;
}
}
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:
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;
return AFPERR_PARAM;
}
}
-
+ AFP_CNID_DONE();
return err;
}
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;
}
/* 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 */
/* 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);
}
/* 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) {
* 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:
#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 {
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,
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,
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 */
#ifdef HAVE_ATFUNCS
opened = of_findnameat(sdir_fd, &path);
#else
- opened = of_findname(&path);
+ opened = of_findname(vol, &path);
#endif /* HAVE_ATFUNCS */
if (opened) {
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);
}
}
/* 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:
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 */
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
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
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));
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 );
}
#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>
struct stat *st;
uint16_t bshort;
struct path *s_path;
- struct stat xxx;
ibuf++;
fork = *ibuf++;
}
}
- if ((opened = of_findname(s_path))) {
+ if ((opened = of_findname(vol, s_path))) {
adsame = opened->of_ad;
}
} else {
eid = ADEID_RFORK;
adflags |= ADFLAGS_RF;
- if (!(access & OPENACC_WR))
- adflags |= ADFLAGS_NORF;
}
if (access & OPENACC_WR) {
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:
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)
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 ) {
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);
"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;
}
}
#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",
* 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;
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:
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 );
}
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)",
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 */
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) {
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:
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);
+++ /dev/null
-/*
- * $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 */
* 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
#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
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;
+(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;
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>
"s switch to non-functioning allocator\n"
"q quit";
- if (!h)
+ if (!h) {
puts("hash_create failed");
+ return 1;
+ }
for (;;) {
if (prompt)
puts("out of memory");
free((void *) key);
free(val);
+ break;
}
if (!hash_alloc_insert(h, key, val)) {
* 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: $
*/
#include "fork.h"
#include "uam_auth.h"
#include "afp_zeroconf.h"
+#include "afpstats.h"
#define AFP_LISTENERS 32
#define FDSET_SAFETY 5
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;
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)
{
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)
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);
}
/* ------------------ */
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;
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 :
#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)) {
int main(int ac, char **av)
{
- fd_set rfds;
- void *ipc;
struct sigaction sv;
sigset_t sigs;
int ret;
/* 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);
}
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 */
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);
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 */
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);
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);
}
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:
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;
/*
- * $Id: mangle.h,v 1.7 2009-10-13 22:55:37 didg Exp $
*
*/
/*
- * $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:
}
/* -------------------------- */
-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;
if (*path->m_name) {
/* not curdir */
- return of_stat (path);
+ return of_stat(vol, path);
}
path->st_errno = 0;
path->st_valid = 1;
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;
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;
}
}
/* -------------------------- */
-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)
/* 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);
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);
/*
- * $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.
#include <atalk/afp.h>
#include <atalk/compat.h>
#include <atalk/unix.h>
+#include <atalk/util.h>
#include "auth.h"
#include "volume.h"
dev_t devno;
static struct mnttab mnt;
- if ( lstat( file, &sb ) < 0 ) {
+ if (stat(file, &sb) < 0) {
return( NULL );
}
devno = sb.st_dev;
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 );
struct mntent *mnt;
int found=0;
- if ( lstat( file, &sb ) < 0 ) {
+ if (stat(file, &sb) < 0 ) {
return( NULL );
}
devno = sb.st_dev;
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;
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;
data += offset;
char *DirectoryNamesCount = data++;
- char *DirectoryNames = data;
size_t size = sizeof(uint8_t);
*DirectoryNamesCount = 0;
}
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,
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);
char *servername_conf;
int header = 0;
char buf[1024], *p;
- FILE *fp = NULL, *randomp;
+ FILE *fp = NULL;
size_t len;
char *server_tmp;
/*
- * $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.
/*
- * $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.
#include <atalk/util.h>
#include <atalk/globals.h>
#include <atalk/volume.h>
+#include <atalk/bstrlib.h>
#include "afp_config.h"
#include "auth.h"
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
/*
- * $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
/*
- * $Id: uid.h,v 1.6 2002-08-30 19:32:41 didg Exp $
* code: jeff@univrel.pr.uconn.edu
*/
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;
}
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) {
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);
/* --------------------- */
-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);
/* ----------------------------- */
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;
* 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) );
}
return( 0 );
}
-
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);
#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>
DIR *dir = NULL;
const struct dirent *entry;
const char *p;
- struct stat st;
long int links;
time_t now = time(NULL);
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",
{
int spaceflag, rc;
uint32_t maxsize;
- VolSpace used;
#ifndef NO_QUOTA_SUPPORT
VolSpace qfree, qtotal;
#endif
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
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 ) {
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
*/
{
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;
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 ) {
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;
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,
ret = AFPERR_MISC;
goto openvol_err;
}
- free(vol_mname);
volume->v_root = dir;
curdir = dir;
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 );
}
cnid_close(volume->v_cdb);
volume->v_cdb = NULL;
}
+ free(vol_mname);
*rbuflen = 0;
return ret;
}
(void)chdir("/");
curdir = NULL;
closevol(obj, vol);
+ server_ipc_volumes(obj);
return( AFP_OK );
}
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);
#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;
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();
}
}
- 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);
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);
}
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");
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)
#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 */
#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[] = {
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.
*/
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;
*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;
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) {
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",
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;
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);
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 */
}
/* 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);
}
/* 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));
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;
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) {
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",
*/
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));
}
/* 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;
}
/*
*/
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;
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;
}
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)) {
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);
/*
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:
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;
}
#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>
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;
static int set_dbdir(const char *dbdir, const char *vpath)
{
EC_INIT;
- int status;
struct stat st;
bstring oldpath, newpath;
char *cmd_argv[4];
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);
}
- 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;
}
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_)
{
int cc;
uid_t uid = 0;
gid_t gid = 0;
- int err = 0;
int debug = 0;
int ret;
sigset_t set;
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;
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;
}
/*
- * $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
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 *);
{
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;
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;
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.");
/*
- * $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.
/*
- * $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.
/*
- * $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.
/*
- * $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.
/*
- * $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
#include <atalk/logger.h>
#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid.h>
#include "pack.h"
#include "dbif.h"
* 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;
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;
}
}
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;
}
}
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;
}
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;
}
}
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;
}
/*
- * $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.
/*
- * $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.
#include <atalk/logger.h>
#include <atalk/util.h>
#include <atalk/errchk.h>
+#include <atalk/cnid.h>
#include "db_param.h"
#include "dbif.h"
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));
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;
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)
{
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;
#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
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);
#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"
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)
{
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)
{
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
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);
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);
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;
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':
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);
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 */
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;
/*
- * $Id: usockfd.h,v 1.5 2009-11-05 14:38:07 franklahm Exp $
*
* Copyright (C) Joerg Lenneis 2003
* All Rights Reserved. See COPYING.
-/*
- * 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 */
/* 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;
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");
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);
/* timer callback */
static void timer_cb(evutil_socket_t fd, short what, void *arg)
{
- static int i = 0;
-
if (in_shutdown)
return;
/* 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);
}
int main(int argc, char **argv)
{
- const char *configfile = NULL;
int c, ret, debug = 0;
sigset_t blocksigs;
struct timeval tv;
}
}
- 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);
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);
}
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);
sigdelset(&blocksigs, SIGTERM);
sigdelset(&blocksigs, SIGQUIT);
sigdelset(&blocksigs, SIGCHLD);
+ sigdelset(&blocksigs, SIGHUP);
sigprocmask(SIG_SETMASK, &blocksigs, NULL);
/* run the event loop */
* 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;
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) {
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);
}
/*
- * $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)
/*
- * $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)
* 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;
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));
}
/* 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);
*rbuflen += sizeof(sessid);
/* public key */
- BN_bn2bin(dh->pub_key, rbuf);
+ BN_bn2bin(dh->pub_key, (unsigned char *)rbuf);
rbuf += KEYSIZE;
*rbuflen += KEYSIZE;
#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);
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))) {
}
/* 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));
cleanup_client_name:
gss_release_name(&status, &client_name);
-
-cleanup_context:
gss_release_buffer(&status, &authenticator_buff);
gss_delete_sec_context(&status, &context, NULL);
/*
- * $Id: uams_guest.c,v 1.18 2009-11-08 01:07:17 didg Exp $
*
* (c) 2001 (see COPYING)
*/
/*
- * $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)
* 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_)
{
/*
- * $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)
/*
- * $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)
Makefile
Makefile.in
*.o
-lockrpc.gen.h
\ No newline at end of file
+afp_dtrace.h
# Makefile.am for include/atalk/
atalkincludedir = $(includedir)/atalk
+BUILT_SOURCES =
+CLEANFILES =
atalkinclude_HEADERS = \
adouble.h \
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
#ifdef HAVE_ACLS
+#define O_NETATALK_ACL (O_NOFOLLOW << 1)
+
#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
#else /* HAVE_ACLS=no */
+#define O_NETATALK_ACL 0
#define chmod_acl chmod
#endif /* HAVE_ACLS */
-/*
+ /*
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
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];
#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)
#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 *);
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);
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 */
--- /dev/null
+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();
+};
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;
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
#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
@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_
#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
#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);
#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 { \
#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"
* Format is network byte order.
*/
#define FCE_PACKET_HEADER_SIZE 8+1+1+4+2
+
struct fce_packet
{
char magic[8];
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"
#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 */
#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)
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];
gid_t admingid;
int volnamelen;
/* default value for winbind authentication */
- char *ntdomain, *ntseparator;
+ char *ntdomain, *ntseparator, *addomain;
char *logconfig;
char *logfile;
char *mimicmodel;
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);
* 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: $
*/
/*--------------------------------------------------------------------------*/
/*
- $Id: iniparser.h,v 1.26 2011-03-02 20:15:13 ndevilla Exp $
- $Revision: 1.26 $
*/
#ifndef _INIPARSER_H_
/* 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;
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 {
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 */
};
#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);
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
#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 */
#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
#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 */
#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 */
/* 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 **);
#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);
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 */
#include <sys/socket.h>
#include <unistd.h>
#include <poll.h>
+#include <sys/stat.h>
#include <atalk/unicode.h>
#include <atalk/bstrlib.h>
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 *
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
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 */
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;
#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 */
#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
.libs
dummy.o
libatalk.abi.tmp
+*dev.abi
# current+1:0:0
#
-VERSION_INFO = 2:0:0
+VERSION_INFO = 5:0:0
# History: VERSION_INFO
#
# 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
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 \
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
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
void uuidcache_dump(void) {
int i;
- int ret = 0;
cacheduser_t *entry;
char timestr[200];
struct tm *tmp = NULL;
char *name = NULL;
unsigned char *uuid = NULL;
cacheduser_t *cacheduser = NULL;
- cacheduser_t *entry;
unsigned char hash;
/* allocate mem and copy values */
/*
- $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
#include <sys/time.h>
#include <string.h>
#include <errno.h>
+#include <ctype.h>
#define LDAP_DEPRECATED 1
#include <ldap.h>
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[] = {
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')) {
#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;
/* 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)
add_cachebyname(name, uuid, mytype, 0);
}
-cleanup:
#ifdef HAVE_LDAP
if (uuid_string) free(uuid_string);
#endif
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 {
}
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;
}
/* ----------------------------- */
-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;
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 */
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));
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;
}
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;
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;
}
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);
{
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)
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) );
}
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;
}
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;
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;
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];
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);
}
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);
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;
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;
}
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;
}
break;
default:
- LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+ LOG(log_error, logtype_ad, "ad_flush: unexpected adouble version");
return -1;
}
}
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)) {
{
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) );
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),
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);
}
}
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)) {
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,
{
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,
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,
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;
}
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,
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);
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");
}
/*!
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));
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;
}
uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
{
uint16_t ret = 0;
- struct ad_fd *adf;
off_t off;
off_t len;
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);
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;
}
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)
}
/* 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);
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);
}
}
}
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;
}
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;
}
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;
}
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) );
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)
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;
}
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;
}
|| 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;
}
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;
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 ));
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;
}
/*!
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_)
{
*
* 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)
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;
}
if (adflags & ADFLAGS_TRUNC)
oflags |= O_TRUNC;
+ if (!(ad->ad_options & ADVOL_FOLLO_SYML))
+ oflags |= O_NOFOLLOW;
+
return oflags;
}
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,
goto EC_CLEANUP;
}
- oflags = O_NOFOLLOW | ad2openflags(ad, ADFLAGS_DF, adflags);
+ oflags = ad2openflags(ad, ADFLAGS_DF, adflags);
admode = mode;
if ((adflags & ADFLAGS_CREATE)) {
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,
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,
}
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);
/*
* 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;
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,
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;
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;
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;
/* 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;
}
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) {
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,
{
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:
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)
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: */
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
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) {
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;
}
p = ad_dir(path);
if (!p)
return -1;
- return lstat( p, stbuf );
+ return stat( p, stbuf );
}
/* ----------------
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);
ad_reso_fileno(ad) = -1;
ad_meta_fileno(ad) = -1;
ad->ad_refcount = 1;
+ ad->ad_rlen = 0;
return;
}
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),
}
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;
}
}
}
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,
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);
}
}
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))
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)
/*
- * $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.
EC_INIT;
struct stat st;
ssize_t cc;
- size_t roundup;
off_t r_off;
if (ad_data_fileno(ad) == AD_SYMLINK) {
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 ) {
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 );
}
/* ------------------------ */
-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) );
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;
}
/*
- $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
*/
bstring brefcstr (char *str) {
bstring b;
- int i;
size_t j;
if (str == NULL)
b->slen = (int) j;
b->mlen = -1;
- b->data = str;
+ b->data = (unsigned char *)str;
return b;
}
/*
- * $Id: cnid_cdb_close.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $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.
/*
- * $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.
/*
- * $Id: cnid_cdb_meta.h,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
*/
#define CNID_META_CNID_LEN 4
/*
- * $Id: cnid_cdb_nextid.c,v 1.2 2005-04-28 20:49:59 bfernhomberg Exp $
*/
#ifdef HAVE_CONFIG_H
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;
}
/*
- * $Id: cnid_cdb_rebuild_add.c,v 1.6 2009-11-20 17:22:11 didg Exp $
*
* All Rights Reserved. See COPYRIGHT.
*
/*
- * $Id: cnid_cdb_resolve.c,v 1.5 2009-10-29 13:38:16 didg Exp $
*/
#ifdef HAVE_CONFIG_H
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;
+}
/*
- * $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>
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}");
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;
}
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)
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) {
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;
}
db->client_stamp = buffer;
db->stamp_size = len;
- memset(buffer,0, len);
- return 0;
+
+ return cnid_dbd_stamp(db);
}
/* ---------------------- */
}
}
+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",
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 */
/*
- * $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.
cdb->cnid_resolve = cnid_last_resolve;
cdb->cnid_update = cnid_last_update;
cdb->cnid_close = cnid_last_close;
-
+ cdb->cnid_wipe = NULL;
+
return cdb;
}
}
/* 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;
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);
}
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;
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");
/*
- * $Id: cnid_tdb_close.c,v 1.3 2009-11-21 13:38:11 didg Exp $
*/
#ifdef HAVE_CONFIG_H
/*
- * $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.
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)
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);
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)
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;
}
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) {
}
/* 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) {
/*
- * $Id: cnid_tdb_nextid.c,v 1.2 2005-04-28 20:50:02 bfernhomberg Exp $
*/
#ifdef HAVE_CONFIG_H
cdb->cnid_resolve = cnid_tdb_resolve;
cdb->cnid_update = cnid_tdb_update;
cdb->cnid_close = cnid_tdb_close;
-
+ cdb->cnid_wipe = NULL;
+
return cdb;
}
* 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");
/*
- * $Id: cnid_tdb_resolve.c,v 1.4 2009-11-22 14:14:05 franklahm Exp $
*/
#ifdef HAVE_CONFIG_H
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)
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);
/* 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) {
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;
/* 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)) {
/*
- * $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.
/*
- * $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.
/*
- * $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:
*
/*
- * $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.
/*
- * $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.
/*
- * $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) {
/* 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;
/* 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;
/* 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;
/*
- * $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.
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;
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);
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;
}
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);
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
#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
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) {
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;
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",
{
char block[DSI_BLOCKSIZ];
struct iovec iov[2];
+ int iovecs = 2;
size_t towrite;
ssize_t len;
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 */
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;
}
}
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;
/*
- * $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.
/*
- * $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.
@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>
*/
/*---------------------------- Includes ------------------------------------*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
#include <ctype.h>
#include <atalk/iniparser.h>
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));
char line [ASCIILINESZ+1] ;
char section [ASCIILINESZ+1] ;
char key [ASCIILINESZ+1] ;
- char tmp [ASCIILINESZ+1] ;
char val [ASCIILINESZ+1] ;
int last=0 ;
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])))) {
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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)
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
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 !!!!
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>
#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)
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)
* 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;
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;
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;
}
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--;
+ }
}
}
}
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];
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) {
}
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;
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;
/*
- * $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
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++;
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++;
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;
}
/* Binary Search for BMP */
- else if (result = do_precomposition(base, comb)) {
+ else if ((result = do_precomposition(base, comb))) {
base = result;
}
cnid.c \
fault.c \
getiface.c \
+ gettok.c \
locking.c \
logger.c \
module.c \
#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>
--- /dev/null
+/*
+ *
+ * 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 */
/*
- $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
"CNID", \
"AFPDaemon", \
"DSI", \
- "ATalkDaemon", \
- "PAPDaemon", \
"UAMS", \
- "end_of_list_marker"} \
+ "FCE", \
+ "ad", \
+ "end_of_list_marker"}
/* =========================================================================
Config
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);
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));
}
/*
- * $Id: module.c,v 1.5 2003-02-17 02:03:12 srittau Exp $
*/
#ifdef HAVE_CONFIG_H
#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
/*!
* 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
*/
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)
*/
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;
*/
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;
* @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
*/
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.
+ */
}
/*
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");
}
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);
}
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)
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 {
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)
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)
#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;
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))
/* 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, "???");
/* 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;
}
/* 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, "???");
/* 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,
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;
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;
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");
/* 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");
char errbuf[1024];
regerror(regexerr, ®, errbuf, sizeof(errbuf));
LOG(log_debug, logtype_default, "readvolfile: bad basedir regex: %s", errbuf);
+ continue;
}
- if (regexec(®, 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(®, 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)
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
**************************************************************/
}
/*!
- * 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);
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);
}
/*!
* @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;
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;
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) );
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);
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;
}
}
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;
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
*
* (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
*
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;
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);
}
/* (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;
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];
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))
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))
/* 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");
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);
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 {
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);
options->volcodepage = strdup(options->unixcodepage);
} else {
if (strcasecmp(p, "UTF-8") == 0) {
- p = strdup("UTF8");
+ p = "UTF8";
}
options->volcodepage = strdup(p);
}
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;
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();
+}
/*
* 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
#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;
}
}
/* 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;
}
* 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. */
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;
}
* 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);
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);
/* 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
*/
#include <errno.h>
#include <signal.h>
#include <time.h>
+#include <pthread.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
} 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;
memcpy (&pid, ipc->msg, sizeof(pid_t));
return server_child_transfer_session(children,
- CHILD_DSIFORK,
pid,
ipc->uid,
ipc->afp_socket,
}
/* ----------------- */
-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;
ipc->child_pid, ipc->uid, boottime);
server_child_kill_one_by_id(children,
- CHILD_DSIFORK,
ipc->child_pid,
ipc->uid,
idlen,
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
*
* @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;
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:
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;
return 0;
}
+
+int ipc_child_state(AFPObj *obj, uint16_t state)
+{
+ return ipc_child_write(obj->ipc_fd, IPC_STATE, sizeof(uint16_t), &state);
+}
*/
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)
* @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
*/
}
/* 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;
#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
+#include <libgen.h>
#include <atalk/adouble.h>
#include <atalk/ea.h>
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 /
}
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;
+}
/* 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);
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;
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;
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));
}
}
- 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:
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:
}
/* 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:
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:
}
/* 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:
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:
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",
EC_FAIL;
default:
LOG(log_debug, logtype_default, "open(\"%s\"): %s", fullpathname(path), strerror(errno));
- errno = ENOATTR;
EC_FAIL;
}
}
EC_FAIL;
default:
LOG(log_debug, logtype_default, "openat(\"%s\"): %s", fullpathname(path), strerror(errno));
- errno = ENOATTR;
EC_FAIL;
}
}
switch (errno) {
case ENOENT:
case EEXIST:
+ case EACCES:
break;
default:
LOG(log_debug, logtype_default, "openat(\"%s\"): %s",
}
/* --------------------- */
-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 */
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;
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)
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
*
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;
}
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;
}
}
/* ----------------- */
-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;
/* 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)) {
}
/* ----------------- */
-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? */
}
}
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);
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)) {
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 :
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)
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;
}
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
}
/* 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;
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;
/* 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;
/* 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
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;
}
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;
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 = \
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
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)
doxygen $(srcdir)/Doxyfile
FORCE:
-DISTCLEANFILES = *~ libevent.pc ./include/event2/event-config.h
+DISTCLEANFILES = *~ ./include/event2/event-config.h
+
+install:
EXTRA_DIST = \
afs-check.m4 \
+ ax_pthread.m4 \
cnid-backend.m4 \
config-checks.m4 \
db3-check.m4 \
summary.m4 \
tcp-wrappers.m4 \
util.m4
-
-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], [
--- /dev/null
+# ===========================================================================
+# 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
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
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])
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])
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], [
)
])
-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]]],
;;
"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])
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
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)
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"
])
AC_SUBST(LDAP_LDFLAGS)
AC_SUBST(LDAP_LIBS)
CFLAGS="$save_CFLAGS"
-LDLFLAGS="$save_LDLFLAGS"
+LDFLAGS="$save_LDFLAGS"
LIBS="$save_LIBS"
])
-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], [
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
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)
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
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
-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.
-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], [
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])
])
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])
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
])
-dnl $Id: tcp-wrappers.m4,v 1.4 2008-08-11 20:44:03 didg Exp $
AC_DEFUN([AC_NETATALK_TCP_WRAPPERS], [
check=maybe
Makefile
Makefile.in
-.gitignore
*.o
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
# 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)
+++ /dev/null
-'\" 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)\&.
--- /dev/null
+'\" 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)\&.
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
+++ /dev/null
-'\" 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)\&.
--- /dev/null
+'\" 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)\&.
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
--- /dev/null
+'\" 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>\&.
+++ /dev/null
-'\" 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>\&.
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
-.so man1/megatron.1
+.so megatron.1
-.so man1/megatron.1
+.so megatron.1
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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\&.
--- /dev/null
+'\" 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\&.
-.so man1/megatron.1
+.so megatron.1
-.so man1/megatron.1
+.so megatron.1
-.so man1/megatron.1
+.so megatron.1
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
-.so man1/megatron.1
+.so megatron.1
Makefile
Makefile.in
*.5
-*.o
# 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
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
## 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)
--- /dev/null
+'\" 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)\&.
+++ /dev/null
-'\" 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)\&.
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)
+++ /dev/null
-'\" 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)
--- /dev/null
+'\" 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)\&.
+++ /dev/null
-'\" 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)\&.
$(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 \
@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
/*
- $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
/*
- $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
/*
- $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
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;
/*
- $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