/*
 * $Id: desktop.c,v 1.26.2.4.2.18.2.2 2005/02/10 01:23:10 didg Exp $
 *
 * See COPYRIGHT.
 *
 * bug:
 * afp_XXXcomment are (the only) functions able to open
 * a ressource fork when there's no data fork, eg after
 * it was removed with samba.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <errno.h>
#include <dirent.h>

#include <atalk/adouble.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netatalk/at.h>
#include <netatalk/endian.h>
#include <atalk/dsi.h>
#include <atalk/atp.h>
#include <atalk/asp.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/logger.h>
#include "volume.h"
#include "directory.h"
#include "fork.h"
#include "globals.h"
#include "desktop.h"
#include "mangle.h"


int afp_opendt(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol	*vol;
    u_int16_t	vid;

    ibuf += 2;

    memcpy( &vid, ibuf, sizeof(vid));
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        *rbuflen = 0;
        return( AFPERR_PARAM );
    }

    memcpy( rbuf, &vid, sizeof(vid));
    *rbuflen = sizeof(vid);
    return( AFP_OK );
}

int afp_closedt(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    *rbuflen = 0;
    return( AFP_OK );
}

struct savedt	si = { { 0, 0, 0, 0 }, -1, 0, 0 };

static char *icon_dtfile(struct vol *vol, u_char creator[ 4 ])
{
    return dtfile( vol, creator, ".icon" );
}

static int iconopen( vol, creator, flags, mode )
struct vol	*vol;
u_char	creator[ 4 ];
int flags;
int mode;
{
    char	*dtf, *adt, *adts;

    if ( si.sdt_fd != -1 ) {
        if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
                si.sdt_vid == vol->v_vid ) {
            return 0;
        }
        close( si.sdt_fd );
        si.sdt_fd = -1;
    }

    dtf = icon_dtfile( vol, creator);

    if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
        if ( errno == ENOENT && ( flags & O_CREAT )) {
            if (( adts = strrchr( dtf, '/' )) == NULL ) {
                return -1;
            }
            *adts = '\0';
            if (( adt = strrchr( dtf, '/' )) == NULL ) {
                return -1;
            }
            *adt = '\0';
            (void) ad_mkdir( dtf, DIRBITS | 0777 );
            *adt = '/';
            (void) ad_mkdir( dtf, DIRBITS | 0777 );
            *adts = '/';

            if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
                LOG(log_error, logtype_afpd, "iconopen(%s): open: %s", dtf, strerror(errno) );
                return -1;
            }
        } else {
            return -1;
        }
    }

    memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
    si.sdt_vid = vol->v_vid;
    si.sdt_index = 1;
    return 0;
}

int afp_addicon(obj, ibuf, ibuflen, rbuf, rbuflen)
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol		*vol;
#ifndef NO_DDP
    struct iovec	iov[ 2 ];
#endif
    u_char		fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
    int			itype, cc = AFP_OK, iovcnt = 0, buflen;
    u_int32_t           ftype, itag;
    u_int16_t		bsize, rsize, vid;

    buflen = *rbuflen;
    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        cc = AFPERR_PARAM;
        goto addicon_err;
    }

    memcpy( fcreator, ibuf, sizeof( fcreator ));
    ibuf += sizeof( fcreator );

    memcpy( &ftype, ibuf, sizeof( ftype ));
    ibuf += sizeof( ftype );

    itype = (unsigned char) *ibuf;
    ibuf += 2;

    memcpy( &itag, ibuf, sizeof( itag ));
    ibuf += sizeof( itag );

    memcpy( &bsize, ibuf, sizeof( bsize ));
    bsize = ntohs( bsize );

    if ( si.sdt_fd != -1 ) {
        (void)close( si.sdt_fd );
        si.sdt_fd = -1;
    }

    if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
        cc = AFPERR_NOITEM;
        goto addicon_err;
    }

    if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
        close(si.sdt_fd);
        si.sdt_fd = -1;
        LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
        cc = AFPERR_PARAM;
        goto addicon_err;
    }

    /*
     * Read icon elements until we find a match to replace, or
     * we get to the end to insert.
     */
    p = imh;
    memcpy( p, &itag, sizeof( itag ));
    p += sizeof( itag );
    memcpy( p, &ftype, sizeof( ftype ));
    p += sizeof( ftype );
    *p++ = itype;
    *p++ = 0;
    bsize = htons( bsize );
    memcpy( p, &bsize, sizeof( bsize ));
    bsize = ntohs( bsize );
    while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
        memcpy( &rsize, irh + 10, sizeof( rsize ));
        rsize = ntohs( rsize );
        /*
         * Is this our set of headers?
         */
        if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
            /*
             * Is the size correct?
             */
            if ( bsize != rsize )
                cc = AFPERR_ITYPE;
            break;
        }

        if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
            LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator),strerror(errno) );
            cc = AFPERR_PARAM;
        }
    }

    /*
     * Some error occurred, return.
     */
addicon_err:
    if ( cc < 0 ) {
        if (obj->proto == AFPPROTO_DSI) {
            dsi_writeinit(obj->handle, rbuf, buflen);
            dsi_writeflush(obj->handle);
        }
        return cc;
    }

    switch (obj->proto) {
#ifndef NO_DDP
    case AFPPROTO_ASP:
        buflen = bsize;
        if ((asp_wrtcont(obj->handle, rbuf, &buflen) < 0) || buflen != bsize)
            return( AFPERR_PARAM );

#ifdef DEBUG1
        if (obj->options.flags & OPTION_DEBUG) {
            printf("(write) len: %d\n", buflen);
            bprint(rbuf, buflen);
        }
#endif

        /*
         * We're at the end of the file, add the headers, etc.  */
        if ( cc == 0 ) {
            iov[ 0 ].iov_base = (caddr_t)imh;
            iov[ 0 ].iov_len = sizeof( imh );
            iov[ 1 ].iov_base = rbuf;
            iov[ 1 ].iov_len = bsize;
            iovcnt = 2;
        }

        /*
         * We found an icon to replace.
         */
        if ( cc > 0 ) {
            iov[ 0 ].iov_base = rbuf;
            iov[ 0 ].iov_len = bsize;
            iovcnt = 1;
        }

        if ( writev( si.sdt_fd, iov, iovcnt ) < 0 ) {
            LOG(log_error, logtype_afpd, "afp_addicon(%s): writev: %s", icon_dtfile(vol, fcreator), strerror(errno) );
            return( AFPERR_PARAM );
        }
        break;
#endif /* no afp/asp */      
    case AFPPROTO_DSI:
        {
            DSI *dsi = obj->handle;

            iovcnt = dsi_writeinit(dsi, rbuf, buflen);

            /* add headers at end of file */
            if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
                LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
                dsi_writeflush(dsi);
                return AFPERR_PARAM;
            }

            if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
                LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
                dsi_writeflush(dsi);
                return AFPERR_PARAM;
            }

            while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
#ifdef DEBUG1
                if ( obj->options.flags & OPTION_DEBUG ) {
                    printf("(write) command cont'd: %d\n", iovcnt);
                    bprint(rbuf, iovcnt);
                }
#endif
                if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
                    LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
                    dsi_writeflush(dsi);
                    return AFPERR_PARAM;
                }
            }
        }
        break;
    }

    close( si.sdt_fd );
    si.sdt_fd = -1;
    return( AFP_OK );
}

static u_char	utag[] = { 0, 0, 0, 0 };
static u_char	ucreator[] = { 0, 0, 0, 0 };/* { 'U', 'N', 'I', 'X' };*/
static u_char	utype[] = { 0, 0, 0, 0 };/* { 'T', 'E', 'X', 'T' };*/
static short	usize = 256;
static u_char	uicon[] = {
    0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
    0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
    0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
    0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
    0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
    0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
    0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
    0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
    0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
    0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
    0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
    0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
    0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
    0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
    0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
    0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
    0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
    0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
    0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
};

int afp_geticoninfo(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol	*vol;
    u_char	fcreator[ 4 ], ih[ 12 ];
    u_int16_t	vid, iindex, bsize;

    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        return( AFPERR_PARAM );
    }

    memcpy( fcreator, ibuf, sizeof( fcreator ));
    ibuf += sizeof( fcreator );
    memcpy( &iindex, ibuf, sizeof( iindex ));
    iindex = ntohs( iindex );

    if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
        if ( iindex > 1 ) {
            return( AFPERR_NOITEM );
        }
        memcpy( ih, utag, sizeof( utag ));
        memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
        *( ih + sizeof( utag ) + sizeof( utype )) = 1;
        *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
        memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
                sizeof( usize ));
        memcpy( rbuf, ih, sizeof( ih ));
        *rbuflen = sizeof( ih );
        return( AFP_OK );
    }

    if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
        return( AFPERR_NOITEM );
    }

    if ( iindex < si.sdt_index ) {
        if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
            return( AFPERR_PARAM );
        }
        si.sdt_index = 1;
    }

    /*
     * Position to the correct spot.
     */
    for (;;) {
        if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
            close( si.sdt_fd );
            si.sdt_fd = -1;
            return( AFPERR_NOITEM );
        }
        memcpy( &bsize, ih + 10, sizeof( bsize ));
        bsize = ntohs(bsize);
        if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
            LOG(log_error, logtype_afpd, "afp_iconinfo(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
            return( AFPERR_PARAM );
        }
        if ( si.sdt_index == iindex ) {
            memcpy( rbuf, ih, sizeof( ih ));
            *rbuflen = sizeof( ih );
            return( AFP_OK );
        }
        si.sdt_index++;
    }
}


int afp_geticon(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol	*vol;
    off_t       offset;
    int		rc, buflen;
    u_char	fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
    u_int16_t	vid, bsize, rsize;

    buflen = *rbuflen;
    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        return( AFPERR_PARAM );
    }

    memcpy( fcreator, ibuf, sizeof( fcreator ));
    ibuf += sizeof( fcreator );
    memcpy( ftype, ibuf, sizeof( ftype ));
    ibuf += sizeof( ftype );
    itype = (unsigned char) *ibuf++;
    ibuf++;
    memcpy( &bsize, ibuf, sizeof( bsize ));
    bsize = ntohs( bsize );

    if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
            memcmp( ftype, utype, sizeof( utype )) == 0 &&
            itype == 1 &&
            bsize <= usize ) {
        memcpy( rbuf, uicon, bsize);
        *rbuflen = bsize;
        return( AFP_OK );
    }

    if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
        return( AFPERR_NOITEM );
    }

    if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
        close(si.sdt_fd);
        si.sdt_fd = -1;
        LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno));
        return( AFPERR_PARAM );
    }

    si.sdt_index = 1;
    offset = 0;
    while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
        si.sdt_index++;
        offset += sizeof(ih);
        if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
                *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
            break;
        }
        memcpy( &rsize, ih + 10, sizeof( rsize ));
        rsize = ntohs( rsize );
        if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
            LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
            return( AFPERR_PARAM );
        }
        offset += rsize;
    }

    if ( rc < 0 ) {
        LOG(log_error, logtype_afpd, "afp_geticon(%s): read: %s", icon_dtfile(vol, fcreator), strerror(errno));
        return( AFPERR_PARAM );
    }

    if ( rc == 0 ) {
        return( AFPERR_NOITEM );
    }

    memcpy( &rsize, ih + 10, sizeof( rsize ));
    rsize = ntohs( rsize );
#define min(a,b)	((a)<(b)?(a):(b))
    rc = min( bsize, rsize );

    if ((obj->proto == AFPPROTO_DSI) && (buflen < rc)) {
        DSI *dsi = obj->handle;
        struct stat st;
        off_t size;

        size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
        if (size < rc + offset) {
            return AFPERR_PARAM;
        }

        if ((*rbuflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
            goto geticon_exit;

        /* do to the streaming nature, we have to exit if we encounter
         * a problem. much confusion results otherwise. */
        while (*rbuflen > 0) {
#ifdef WITH_SENDFILE
            if (!obj->options.flags & OPTION_DEBUG) {
                if (sys_sendfile(dsi->socket, si.sdt_fd, &offset, dsi->datasize) < 0) {
                    switch (errno) {
                    case ENOSYS:
                    case EINVAL:  /* there's no guarantee that all fs support sendfile */
                        break;
                    default:
                        goto geticon_exit;
                    }
                }
                goto geticon_done;
            }
#endif
            buflen = read(si.sdt_fd, rbuf, *rbuflen);
            if (buflen < 0)
                goto geticon_exit;

#ifdef DEBUG1
            if (obj->options.flags & OPTION_DEBUG) {
                printf( "(read) reply: %d, %d\n", buflen, dsi->clientID);
                bprint(rbuf, buflen);
            }
#endif
            /* dsi_read() also returns buffer size of next allocation */
            buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
            if (buflen < 0)
                goto geticon_exit;

            *rbuflen = buflen;
        }

geticon_done:
        dsi_readdone(dsi);
        return AFP_OK;

geticon_exit:
        LOG(log_info, logtype_afpd, "afp_geticon(%s): %s", icon_dtfile(vol, fcreator), strerror(errno));
        dsi_readdone(dsi);
        obj->exit(EXITERR_SYS);
        return AFP_OK;

    } else {
        if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
            return( AFPERR_PARAM );
        }
        *rbuflen = rc;
    }
    return AFP_OK;
}

/* ---------------------- */
static char		hexdig[] = "0123456789abcdef";
char *dtfile(const struct vol *vol, u_char creator[], char *ext )
{
    static char	path[ MAXPATHLEN + 1];
    char	*p;
    unsigned int i;

    strcpy( path, vol->v_path );
    strcat( path, "/.AppleDesktop/" );
    for ( p = path; *p != '\0'; p++ )
        ;

    if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
        *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
        *p++ = hexdig[ creator[ 0 ] & 0x0f ];
    } else {
        *p++ = creator[ 0 ];
    }

    *p++ = '/';

    for ( i = 0; i < sizeof( CreatorType ); i++ ) {
        if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
            *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
            *p++ = hexdig[ creator[ i ] & 0x0f ];
        } else {
            *p++ = creator[ i ];
        }
    }
    *p = '\0';
    strcat( path, ext );

    return( path );
}

/* ---------------------------
 * mpath is only a filename 
 * did filename parent directory ID.
*/
static char  upath[ MAXPATHLEN + 1];
static char  mpath[ MAXPATHLEN + 1];

char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
{
    char	*m, *u;
    size_t       inplen;
    size_t       outlen;
    u_int16_t	 flags = 0;
        
    if ( *mpath == '\0' ) {
        return( "." );
    }

    /* set conversion flags */
    if (!(vol->v_flags & AFPVOL_NOHEX))
        flags |= CONV_ESCAPEHEX;
    if (!(vol->v_flags & AFPVOL_USEDOTS))
        flags |= CONV_ESCAPEDOTS;

    if ((vol->v_casefold & AFPVOL_MTOUUPPER))
        flags |= CONV_TOUPPER;
    else if ((vol->v_casefold & AFPVOL_MTOULOWER))
        flags |= CONV_TOLOWER;

    m = demangle(vol, mpath, did);
    if (m != mpath) {
        return m;
    }

    m = mpath;
    u = upath;

    inplen = strlen(m);
    outlen = MAXPATHLEN;

    if ((size_t)-1 == (outlen = convert_charset ( (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_volcharset, vol->v_maccharset, m, inplen, u, outlen, &flags)) ) {
        LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.", (utf8)?"UTF8-MAC":vol->v_maccodepage, vol->v_volcodepage, mpath);
	    return NULL;
    }
    upath[outlen] = 0;

#ifdef DEBUG
    LOG(log_debug, logtype_afpd, "mtoupath: '%s':'%s'", mpath, upath);
#endif /* DEBUG */
    return( upath );
}

/* --------------- 
 * id filename ID
*/
char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
{
    char        *m, *u;
    u_int16_t    flags = CONV_IGNORE | CONV_UNESCAPEHEX;
    size_t       outlen;

    m = mpath;
    outlen = strlen(upath);

    if (vol->v_casefold & AFPVOL_UTOMUPPER)
        flags |= CONV_TOUPPER;
    else if (vol->v_casefold & AFPVOL_UTOMLOWER)
        flags |= CONV_TOLOWER;

    u = upath;

    /* convert charsets */
    if ((size_t)-1 == ( outlen = convert_charset ( vol->v_volcharset, (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_maccharset, u, outlen, mpath, MAXPATHLEN, &flags)) ) { 
        LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.", vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
	goto utompath_error;
    }

    mpath[outlen] = 0; 
    if (!(flags & CONV_REQMANGLE)) 
        flags = 0;
    else
        flags = 1;

    if (utf8)
        flags |= 2;

    m = mangle(vol, mpath, outlen, upath, id, flags);

#ifdef DEBUG
    LOG(log_debug, logtype_afpd, "utompath: '%s':'%s':'%2.2X'", upath, m, ntohl(id));
#endif /* DEBUG */
    return(m);

utompath_error:
    u = "???";
    m = mangle(vol, u, strlen(u), upath, id, (utf8)?3:1);
    return(m);
}

/* ------------------------- */
static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf)
{
    struct ofork        *of;
    char                *name, *upath;
    int                 isadir;
    int			clen;
    struct adouble	ad, *adp;

    clen = (u_char)*ibuf++;
    clen = min( clen, 199 );

    upath = path->u_name;
    if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
        return AFPERR_ACCESS;
    }
    
    isadir = path_isadir(path);
    if (isadir || !(of = of_findname(path))) {
        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
        adp = &ad;
    } else
        adp = of->of_ad;

    if (ad_open( upath , vol_noadouble(vol) |
                 (( isadir) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
                 O_RDWR|O_CREAT, 0666, adp) < 0 ) {
        return( AFPERR_ACCESS );
    }

    if (ad_getentryoff(adp, ADEID_COMMENT)) {
        if ( (ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
            if ( *path->m_name == '\0' ) {
                name = curdir->d_m_name;
            } else {
                name = path->m_name;
            }
            ad_setname(adp, name);
        }
        ad_setentrylen( adp, ADEID_COMMENT, clen );
        memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
        ad_flush( adp, ADFLAGS_HF );
    }
    ad_close( adp, ADFLAGS_HF );
    return( AFP_OK );
}

/* ----------------------------- */
int afp_addcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol		*vol;
    struct dir		*dir;
    struct path         *path;
    u_int32_t           did;
    u_int16_t		vid;

    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        return( AFPERR_PARAM );
    }

    memcpy( &did, ibuf, sizeof( did ));
    ibuf += sizeof( did );
    if (NULL == ( dir = dirlookup( vol, did )) ) {
	return afp_errno;
    }

    if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
	return get_afp_errno(AFPERR_NOOBJ);
    }

    if ((u_long)ibuf & 1 ) {
        ibuf++;
    }

    return ad_addcomment(vol, path, ibuf);
}

/* -------------------- */
static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, int *rbuflen)
{
    struct adouble	ad, *adp;
    struct ofork        *of;
    char		*upath;
    int                 isadir;


    upath = path->u_name;
    isadir = path_isadir(path);
    if (isadir || !(of = of_findname(path))) {
        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
        adp = &ad;
    } else
        adp = of->of_ad;
        
    if ( ad_metadata( upath,( isadir) ? ADFLAGS_DIR : 0, adp) < 0 ) {
        return( AFPERR_NOITEM );
    }

    if (!ad_getentryoff(adp, ADEID_COMMENT)) {
        return AFPERR_NOITEM;
    }
    /*
     * Make sure the AD file is not bogus.
     */
    if ( ad_getentrylen( adp, ADEID_COMMENT ) <= 0 ||
            ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
        ad_close( adp, ADFLAGS_HF );
        return( AFPERR_NOITEM );
    }

    *rbuf++ = ad_getentrylen( adp, ADEID_COMMENT );
    memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ),
            ad_getentrylen( adp, ADEID_COMMENT ));
    *rbuflen = ad_getentrylen( adp, ADEID_COMMENT ) + 1;
    ad_close( adp, ADFLAGS_HF );

    return( AFP_OK );
}

/* -------------------- */
int afp_getcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol		*vol;
    struct dir		*dir;
    struct path         *s_path;
    u_int32_t		did;
    u_int16_t		vid;
    
    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        return( AFPERR_PARAM );
    }

    memcpy( &did, ibuf, sizeof( did ));
    ibuf += sizeof( did );
    if (NULL == ( dir = dirlookup( vol, did )) ) {
	return afp_errno;
    }

    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
	return get_afp_errno(AFPERR_NOOBJ);
    }

    return ad_getcomment(vol, s_path, rbuf, rbuflen);
}

/* ----------------------- */
static int ad_rmvcomment(struct vol *vol, struct path *path)
{
    struct adouble	ad, *adp;
    struct ofork        *of;
    int                 isadir;
    char		*upath;

    upath = path->u_name;
    if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
        return AFPERR_ACCESS;
    }

    isadir = path_isadir(path);
    if (isadir || !(of = of_findname(path))) {
        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
        adp = &ad;
    } else
        adp = of->of_ad;

    if ( ad_open( upath,
                   (isadir) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF,
                  O_RDWR, 0, adp) < 0 ) {
        switch ( errno ) {
        case ENOENT :
            return( AFPERR_NOITEM );
        case EACCES :
            return( AFPERR_ACCESS );
        default :
            return( AFPERR_PARAM );
        }
    }

    if (ad_getentryoff(adp, ADEID_COMMENT)) {
        ad_setentrylen( adp, ADEID_COMMENT, 0 );
        ad_flush( adp, ADFLAGS_HF );
    }
    ad_close( adp, ADFLAGS_HF );
    return( AFP_OK );
}

/* ----------------------- */
int afp_rmvcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
AFPObj      *obj;
char	*ibuf, *rbuf;
int		ibuflen, *rbuflen;
{
    struct vol		*vol;
    struct dir		*dir;
    struct path         *s_path;
    u_int32_t		did;
    u_int16_t		vid;

    *rbuflen = 0;
    ibuf += 2;

    memcpy( &vid, ibuf, sizeof( vid ));
    ibuf += sizeof( vid );
    if (NULL == ( vol = getvolbyvid( vid )) ) {
        return( AFPERR_PARAM );
    }

    memcpy( &did, ibuf, sizeof( did ));
    ibuf += sizeof( did );
    if (NULL == ( dir = dirlookup( vol, did )) ) {
	return afp_errno;
    }

    if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
	return get_afp_errno(AFPERR_NOOBJ);
    }
    
    return ad_rmvcomment(vol, s_path);
}
