/*
 * Copyright (c) 1990,1993 Regents of The University of Michigan.
 * All Rights Reserved.  See COPYRIGHT.
 */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <netatalk/endian.h>
#include <atalk/afp.h>
#include <strings.h>
#include <stdio.h>
#include "auth.h"
#include "directory.h"
#include "volume.h"

#if defined( BSD4_4 ) || defined( ultrix )
#include <sys/mount.h>
#include <sys/quota.h>
#endif BSD4_4 ultrix

#ifdef _IBMR2
#include <sys/statfs.h>
#endif _IBMR2

#if defined( sun ) || defined( ibm032 ) || defined( linux )
#include <sys/vfs.h>
#endif sun ibm032 linux

#if !defined( linux ) && !defined( ultrix )
#include <ufs/quota.h>
#include <mntent.h>
#endif linux ultrix

#ifdef ibm032
typedef unsigned short	mode_t;
#define dirent		direct
#endif ibm032

#ifdef sun
/*
 * we would include dirent.h, but several machines don't have it
 * the direct and dirent structs are identical on the sun, so:
 */
#define dirent		direct
#ifdef i386
typedef int	mode_t;
#endif i386
#endif sun

ustatfs_getvolspace( vol, bfree, btotal )
    struct vol	*vol;
    int		*bfree, *btotal;
{
#ifdef ultrix
    struct fs_data	sfs;
#else ultrix
    struct statfs	sfs;
#endif ultrix

    if ( statfs( vol->v_path, &sfs ) < 0 ) {
	return( AFPERR_PARAM );
    }

#if defined( BSD4_4 )
    *bfree = sfs.f_bavail * sfs.f_fsize;
#else
#if defined( ultrix )
    *bfree = sfs.fd_req.bfreen * 1024;
#else
    *bfree = sfs.f_bavail * sfs.f_bsize;
#endif ultrix
#endif BSD4_4

    *bfree = htonl( *bfree );

#if defined( BSD4_4 )
    *btotal = ( sfs.f_blocks - sfs.f_bfree + sfs.f_bavail ) * sfs.f_fsize;
#else
#if defined( ultrix )
    *btotal = ( sfs.fd_req.btot - sfs.fd_req.bfree + sfs.fd_req.bfreen )
		* 1024;
#else
    *btotal = ( sfs.f_blocks - sfs.f_bfree + sfs.f_bavail ) * sfs.f_bsize;
#endif ultrix
#endif BSD4_4

    *btotal = htonl( *btotal );

    return( AFP_OK );
}

#ifndef linux
/*
 * Return the block-special device name associated with the filesystem
 * on which "file" resides.  Returns NULL on failure.
 */
#ifdef ultrix

    char *
special( file )
    char *file;
{
    static struct fs_data	fsd;

    if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
	syslog( "special: getmnt %s: %m", file );
	return( NULL );
    }

    return( fsd.fd_req.devname );
}

#else ultrix

    char *
special( file )
    char *file;
{
    struct stat		sb;
    FILE 		*mtab;
    dev_t		devno;
    struct mntent	*mnt;

    if ( stat( file, &sb ) < 0 ) {
	return( NULL );
    }
    devno = sb.st_dev;

    if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
	return( NULL );
    }

    while (( mnt = getmntent( mtab )) != NULL ) {
	if ( stat( mnt->mnt_fsname, &sb ) == 0 && devno == sb.st_rdev ) {
	    break;
	}
    }

    endmntent( mtab );
    if ( mnt ) {
	return( mnt->mnt_fsname );
    } else {
	return( NULL );
    }
}

#endif ultrix

uquota_getvolspace( vol, bfree, btotal )
    struct vol	*vol;
    int		*bfree, *btotal;
{
    struct dqblk	dqblk;

    if ( vol->v_gvs == NULL &&
	    ( vol->v_gvs = special( vol->v_path )) == NULL ) {
	syslog( LOG_ERR, "uquota_getvolspace: special %s fails", vol->v_path );
	return( AFPERR_PARAM );
    }

#ifdef ultrix
    if ( quota( Q_GETDLIM, uuid, vol->v_gvs, &dqblk ) != 0 ) {
	return( AFPERR_PARAM );
    }
#else ultrix
    if ( quotactl( Q_GETQUOTA, vol->v_gvs, uuid, &dqblk ) != 0 ) {
	return( AFPERR_PARAM );
    }
#endif ultrix

    if ( overquota( &dqblk )) {
	*btotal = htonl( dbtob( dqblk.dqb_bhardlimit ));
	*bfree = htonl( dbtob( dqblk.dqb_bhardlimit ) -
		dbtob( dqblk.dqb_curblocks ));
    } else {
	*btotal = htonl( dbtob( dqblk.dqb_bsoftlimit ));
	*bfree = htonl( dbtob( dqblk.dqb_bsoftlimit ) -
		dbtob( dqblk.dqb_curblocks ));
    }

    return( AFP_OK );
}

overquota( dqblk )
    struct dqblk	*dqblk;
{
    struct timeval	tv;

    if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ) {
	return( 0 );
    }
#ifdef ultrix
    if ( dqblk->dqb_bwarn ) {
	return( 0 );
    }
#else ultrix
    if ( gettimeofday( &tv, 0 ) < 0 ) {
	syslog( LOG_ERR, "overquota: gettimeofday: %m" );
	return( AFPERR_PARAM );
    }
    if ( !dqblk->dqb_btimelimit || dqblk->dqb_btimelimit > tv.tv_sec ) {
	return( 0 );
    }
#endif ultrix
    return( 1 );
}

#endif linux

utommode( stat, ma )
    struct stat		*stat;
    struct maccess	*ma;
{
    mode_t		mode;

    mode = stat->st_mode;

    ma->ma_world = utombits( mode );
    mode = mode >> 3;

    ma->ma_group = utombits( mode );
    mode = mode >> 3;

    ma->ma_owner = utombits( mode );

    if ( uuid == stat->st_uid ) {
	ma->ma_user = ma->ma_owner | AR_UOWN;
    } else if ( gmem( stat->st_gid )) {
	ma->ma_user = ma->ma_group;
    } else {
	ma->ma_user = ma->ma_world;
    }

    /*
     * There are certain things the mac won't try if you don't have
     * the "owner" bit set, even tho you can do these things on unix wiht
     * only write permission.  What were the things?
     */
    if ( ma->ma_user & AR_UWRITE ) {
	ma->ma_user |= AR_UOWN;
    }
}

utombits( bits )
    mode_t	bits;
{
    int		mbits;

    mbits = 0;

    mbits |= ( bits & ( S_IREAD >> 6 )) ? AR_UREAD : 0;
    mbits |= ( bits & ( S_IWRITE >> 6 )) ? AR_UWRITE : 0;
    mbits |= ( bits & ( S_IEXEC >> 6 )) ? AR_USEARCH : 0;

    return( mbits );
}

gmem( gid )
    int	gid;
{
    int		i;

    for ( i = 0; i < ngroups; i++ ) {
	if ( groups[ i ] == gid ) {
	    return( 1 );
	}
    }
    return( 0 );
}

mtoumode( ma )
    struct maccess	*ma;
{
    mode_t		mode;

    mode = 0;
    mode |= mtoubits( ma->ma_owner );
    mode = mode << 3;

    mode |= mtoubits( ma->ma_group );
    mode = mode << 3;

    mode |= mtoubits( ma->ma_world );

    return( mode );
}

mtoubits( bits )
    u_char	bits;
{
    mode_t	mode;

    mode = 0;

    mode |= ( bits & AR_UREAD ) ? ( S_IREAD >> 6 ) : 0;
    mode |= ( bits & AR_UWRITE ) ? ( S_IWRITE >> 6 ) : 0;
    mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0;

    return( mode );
}

setdeskmode( mode )
    mode_t	mode;
{
    static char		wd[ MAXPATHLEN ];
    char		c, modbuf[ 12 ], *m;
    struct dirent	*deskp, *subp;
    DIR			*desk, *sub;

    if ( getwd( wd ) == NULL ) {
	return( -1 );
    }
    if ( chdir( ".AppleDesktop" ) < 0 ) {
	return( -1 );
    }
    if (( desk = opendir( "." )) == NULL ) {
	if ( chdir( wd ) < 0 ) {
	    syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
	    exit( 1 );
	}
	return( -1 );
    }
    for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
	if ( strcmp( deskp->d_name, "." ) == 0 ||
#ifdef 0
		strcmp( deskp->d_name, ".." ) == 0 || deskp->d_namlen > 2 ) {
#endif
		strcmp( deskp->d_name, ".." ) == 0 || deskp->d_reclen > 2 ) {
	    continue;
	}
	strcpy( modbuf, deskp->d_name );
	strcat( modbuf, "/" );
	m = index( modbuf, '\0' );
	if (( sub = opendir( deskp->d_name )) == NULL ) {
	    continue;
	}
	for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
	    if ( strcmp( subp->d_name, "." ) == 0 ||
		    strcmp( subp->d_name, ".." ) == 0 ) {
		continue;
	    }
	    *m = '\0';
	    strcat( modbuf, subp->d_name );
	    if ( chmod( modbuf, mode ) < 0 ) {
		syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", modbuf );
	    }
	}
	closedir( sub );
	if ( chmod( deskp->d_name, mode ) < 0 ) {
	    syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", deskp->d_name );
	}
    }
    closedir( desk );
    if ( chdir( wd ) < 0 ) {
	syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
	exit( 1 );
    }
    if ( chmod( ".AppleDesktop", mode ) < 0 ) {
	syslog( LOG_DEBUG, "setdeskmode: chmod .AppleDesktop: %m" );
    }
    return( 0 );
}

setdirmode( mode )
    mode_t	mode;
{
    static char		buf[ MAXPATHLEN ];
    struct stat		st;
    char		*m;
    struct dirent	*dirp;
    DIR			*dir;

    if (( dir = opendir( "." )) == NULL ) {
	syslog( LOG_ERR, "setdirmode: opendir .: %m" );
	return( -1 );
    }
    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
	if ( *dirp->d_name == '.' ) {
	    continue;
	}
	if ( stat( dirp->d_name, &st ) < 0 ) {
	    syslog( LOG_DEBUG, "setdirmode: stat %s: %m", dirp->d_name );
	    continue;
	}
	if (( st.st_mode & S_IFMT ) == S_IFREG ) {
	    if ( chmod( dirp->d_name, mode ) < 0 ) {
		syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", dirp->d_name );
	    }
	}
    }
    closedir( dir );
    if (( dir = opendir( ".AppleDouble" )) == NULL ) {
	syslog( LOG_ERR, "setdirmode: opendir .AppleDouble: %m" );
	return( -1 );
    }
    strcpy( buf, ".AppleDouble" );
    strcat( buf, "/" );
    m = index( buf, '\0' );
    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
	if ( strcmp( dirp->d_name, "." ) == 0 ||
		strcmp( dirp->d_name, ".." ) == 0 ) {
	    continue;
	}
	*m = '\0';
	strcat( buf, dirp->d_name );
	if ( chmod( buf, mode ) < 0 ) {
	    syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", buf );
	}
    }
    closedir( dir );
    if ( chmod( ".AppleDouble", mode ) < 0 ) {
	syslog( LOG_ERR, "setdirmode: chmod .AppleDouble: %m" );
	return( -1 );
    }
    if ( chmod( ".", mode ) < 0 ) {
	syslog( LOG_ERR, "setdirmode: chmod .: %m" );
	return( -1 );
    }
    return( 0 );
}

setdeskowner( uid, gid )
    int		uid;
    int		gid;
{
    static char		wd[ MAXPATHLEN ];
    char		c, modbuf[ 12 ], *m;
    struct dirent	*deskp, *subp;
    DIR			*desk, *sub;

    if ( getwd( wd ) == NULL ) {
	return( -1 );
    }
    if ( chdir( ".AppleDesktop" ) < 0 ) {
	return( -1 );
    }
    if (( desk = opendir( "." )) == NULL ) {
	if ( chdir( wd ) < 0 ) {
	    syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
	    exit( 1 );
	}
	return( -1 );
    }
    for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
	if ( strcmp( deskp->d_name, "." ) == 0 ||
#ifdef 0
		strcmp( deskp->d_name, ".." ) == 0 || deskp->d_namlen > 2 ) {
#endif
		strcmp( deskp->d_name, ".." ) == 0 || deskp->d_reclen > 2 ) {
	    continue;
	}
	strcpy( modbuf, deskp->d_name );
	strcat( modbuf, "/" );
	m = index( modbuf, '\0' );
	if (( sub = opendir( deskp->d_name )) == NULL ) {
	    continue;
	}
	for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
	    if ( strcmp( subp->d_name, "." ) == 0 ||
		    strcmp( subp->d_name, ".." ) == 0 ) {
		continue;
	    }
	    *m = '\0';
	    strcat( modbuf, subp->d_name );
	    if ( chown( modbuf, uid, gid ) < 0 ) {
		syslog( LOG_DEBUG, "setdeskown: chown %s: %m", modbuf );
	    }
	}
	closedir( sub );
	if ( chown( deskp->d_name, uid, gid ) < 0 ) {
	    syslog( LOG_DEBUG, "setdeskowner: chown %s: %m", deskp->d_name );
	}
    }
    closedir( desk );
    if ( chdir( wd ) < 0 ) {
	syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
	exit( 1 );
    }
    if ( chown( ".AppleDesktop", uid, gid ) < 0 ) {
	syslog( LOG_ERR, "setdeskowner: chown .AppleDesktop: %m" );
    }
    return( 0 );
}

setdirowner( uid, gid )
    int		uid;
    int		gid;
{
    static char		buf[ MAXPATHLEN ];
    struct stat		st;
    char		*m;
    struct dirent	*dirp;
    DIR			*dir;

    if (( dir = opendir( "." )) == NULL ) {
	return( -1 );
    }
    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
	if ( *dirp->d_name == '.' ) {
	    continue;
	};
	if ( stat( dirp->d_name, &st ) < 0 ) {
	    syslog( LOG_DEBUG, "setdirowner: stat %s: %m", dirp->d_name );
	    continue;
	}
	if (( st.st_mode & S_IFMT ) == S_IFREG ) {
	    if ( chown( dirp->d_name, uid, gid ) < 0 ) {
		syslog( LOG_DEBUG, "setdirowner: chown %s: %m", dirp->d_name );
	    }
	}
    }
    closedir( dir );
    if (( dir = opendir( ".AppleDouble" )) == NULL ) {
	return( -1 );
    }
    strcpy( buf, ".AppleDouble" );
    strcat( buf, "/" );
    m = index( buf, '\0' );
    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
	if ( strcmp( dirp->d_name, "." ) == 0 ||
		strcmp( dirp->d_name, ".." ) == 0 ) {
	    continue;
	}
	*m = '\0';
	strcat( buf, dirp->d_name );
	if ( chown( buf, uid, gid ) < 0 ) {
	    syslog( LOG_DEBUG, "setdirowner: chown %d/%d %s: %m",
		    uid, gid, buf );
	}
    }
    closedir( dir );

    /*
     * We cheat: we know that chown doesn't do anything.
     */
    if ( stat( ".AppleDouble", &st ) < 0 ) {
	syslog( LOG_ERR, "setdirowner: stat .AppleDouble: %m" );
	return( -1 );
    }
    if ( gid != st.st_gid ) {
	if ( chown( ".AppleDouble", uid, gid ) < 0 ) {
	    syslog( LOG_ERR, "setdirowner: chown %d/%d .AppleDouble: %m",
		    uid, gid );
	    return( -1 );
	}
    }

    if ( stat( ".", &st ) < 0 ) {
	syslog( LOG_ERR, "setdirowner: stat .AppleDouble: %m" );
	return( -1 );
    }
    if ( gid != st.st_gid ) {
	if ( chown( ".", uid, gid ) < 0 ) {
	    syslog( LOG_ERR, "setdirowner: chown %d/%d . : %m", uid, gid );
	    return( -1 );
	}
    }

    return( 0 );
}
