/* Server for the Midnight Commander Virtual File System.
   This has nothing to do with cryptography.
   
   Copyright (C) 1995, 1996, 1997 The Free Software Foundation

   Written by: 
      Miguel de Icaza, 1995, 1997, 
      Andrej Borsenkow 1996.

   Heavily modifed by:
      Paul Sheer 1998, 1999.
   
   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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   TODO:
   opendir instead of keeping its table of file handles could return
   the pointer and expect the client to send a proper value back each
   time :-)

   We should use syslog to register login/logout.
   
    */


/* this file no longer has any code from The Regents of the University    
   of California, so I have taken out their Copyright notice */


/* {{{ Includes and global variables */

#include <config.h>
#include "net-includes.h"
#include "diffie/diffie-socket.h"
#include "diffie/z-socket.h"
#include "mostincludes.h"
#include "mem.h"

#include "mcfs.h"
#include "tcputil.h"
#include "diffie/compat.h"
#include "mad.h"

#ifdef HAVE_WRAP
#include <tcpd.h>
int allow_severity = 1;
int deny_severity = 1;
#endif

#if defined(HAVE_SHADOW)
#   if defined(HAVE_PW_ENCRYPT)
#   elif defined(HAVE_CRYPT)
#   else
#      define OUTPUT_MESSAGE "Unsupported shadow passwords. Please contact mirrordir@mail.obsidian.co.za"
#   endif
#elif defined(HAVE_CRYPT)
#else
#   define OUTPUT_MESSAGE "crypt() not found on your system. Please contact mirrordir@mail.obsidian.co.za"
#endif

#ifndef OUTPUT_MESSAGE

#ifdef HAVE_MAD
#define exit(x) { mad_finalize(__FILE__, __LINE__) ; exit(x) ; }
#endif

/* The socket from which we accept commands */
int msock;

/* client address for login */
struct in_addr client_addr;

char client_user[64] = "nobody";

char *password_server = 0;

/* Requested version number from client */
static int clnt_version;

/* If non zero, we accept further commands */
int logged_in = 0;

static int idle_allow = 180;

/* Home directory */
char *home_dir = NULL;

char *up_dir = NULL;

/* Were we started from inetd? */
int  inetd_started = 0;

/* Are we running as a daemon? */
int  isDaemon = 0;

/* guess */
int verbose = 0;

/* ftp auth */
int ftp = 0;

/* port number in which we listen to connections,
 * if zero, we try to contact the portmapper to get a port, and
 * if it's not possible, then we use a hardcoded value
 */
int portnum = 0;

/* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
int r_auth = 0;

int quick_file_read = 0;

#define OPENDIR_HANDLES 8

#define DO_QUIT_VOID() \
do { \
  quit_server = 1; \
  return_code = 1; \
  return; \
} while (0)

/* Only used by get_port_number */
#define DO_QUIT_NONVOID(a) \
do { \
  quit_server = 1; \
  return_code = 1; \
  return (a); \
} while (0)

char buffer [4096];
int debug = 1;
static int quit_server;
static int return_code;

/* }}} */

/* {{{ Misc routines */

void send_status (int status, int errno_number)
{
    rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
    errno = 0;
}

/* This routine is called by rpc_get/rpc_send when the connection is closed */
void tcp_invalidate_socket (int sock)
{
    if (verbose) printf ("Connection closed\n");
    mad_finalize (__FILE__, __LINE__);
    DO_QUIT_VOID();
}

/* }}} */

/* {{{ File with handle operations */

static pid_t my_pid = 0;

static void wait_for_shared_lock (int f, off_t offset, int size)
{
#ifdef F_RDLCK
    struct flock l;
    l.l_type = F_RDLCK;
    l.l_whence = SEEK_SET;
    l.l_start = offset;
    l.l_len = size;
    l.l_pid = my_pid;
    fcntl (f, F_SETLKW, &l);
#endif
}

int last_open_fd = 0;

void do_open (void)
{
    int handle, flags, mode, n;
    char *arg, *data;

    if (last_open_fd && quick_file_read) {
	close (last_open_fd);
	last_open_fd = 0;
    }
    rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode, RPC_END);
    handle = open (arg, flags, mode);
    if (flags == O_RDONLY)	/* FIXED: downloading files needs lock, uploading will
				   probably be to unused directories, hence no lock is needed */
	wait_for_shared_lock (handle, 0, 0x7FFFFF0);
    send_status (handle, errno);
    free (arg);
    if (handle < 0)
	return;
    last_open_fd = handle;
    if (!quick_file_read)
	return;
    data = malloc (65536);
    if (!data) {
	send_status (-1, ENOMEM);
	free (data);
	return;
    }
    n = read (handle, data, 65536);
    if (n < 0) {
	send_status (-1, errno);
	free (data);
	return;
    }
    send_status (n, 0);
    rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
    free (data);
}

void do_read (void)
{
    int handle, count, n;
    void *data;
   
    rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
    data = malloc (count);
    if (!data){
	send_status (-1, ENOMEM);
	return;
    }
    if (verbose) printf ("count=%d\n", count);
    n = read (handle, data, count);
    if (verbose) printf ("result=%d\n", n);
    if (n < 0){
	send_status (-1, errno);
	return;
    }
    send_status (n, 0);
    rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
    
    free (data);
}

void do_write (void)
{
    int handle, count, status, written = 0;
    char buf[8192];

    rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
    status = 0;
    while (count) {
	int nbytes = count > 8192 ? 8192 : count;

	rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
	status = write (handle, buf, nbytes);
	if (status < 0) {
	    send_status (status, errno);
	    return;
	}
	/* FIXED: amount written must be returned to caller */
	written += status;
	if (status < nbytes) {
	    send_status (written, errno);
	    return;
	}
	count -= nbytes;
    }
    send_status (written, errno);
}

void do_lseek (void)
{
    int handle, offset, whence, status;

    rpc_get (msock,
	     RPC_INT, &handle,
	     RPC_INT, &offset,
	     RPC_INT, &whence, RPC_END);
    status = lseek (handle, offset, whence);
    send_status (status, errno);
}

void do_close (void)
{
    int handle, status;
    
    rpc_get (msock, RPC_INT, &handle, RPC_END);
    status = close (handle);
    send_status (status, errno);
}

/* }}} */

/* {{{ Stat family routines */

void send_time (int sock, time_t t)
{
    if (clnt_version == 1) {
	char   *ct;
	int    month;
    
	ct = ctime (&t);
	ct [3] = ct [10] = ct [13] = ct [16] = ct [19] = 0;
	
	/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
	if (ct [4] == 'J'){
	    if (ct [5] == 'a'){
		month = 0;
	    } else
		month = (ct [6] == 'n') ? 5 : 6;
	} else if (ct [4] == 'F'){
	    month = 1;
	} else if (ct [4] == 'M'){
	    month = (ct [6] == 'r') ? 2 : 5;
	} else if (ct [4] == 'A'){
	    month = (ct [5] == 'p') ? 3 : 7;
	} else if (ct [4] == 'S'){
	    month = 8;
	} else if (ct [4] == 'O'){
	    month = 9;
	} else if (ct [4] == 'N'){
	    month = 10;
	} else
	month = 11;
	rpc_send (msock,
		  RPC_INT, atoi (&ct [17]),  	/* sec */
		  RPC_INT, atoi (&ct [14]),         /* min */
		  RPC_INT, atoi (&ct [11]),         /* hour */
		  RPC_INT, atoi (&ct [8]),          /* mday */
		  RPC_INT, atoi (&ct [20]),         /* year */
		  RPC_INT, month,	                /* month */
		  RPC_END);
    } else {
	long ltime = (long) t;
	char buf[2*sizeof(long) + 1];

	sprintf (buf, "%lx", ltime);
	rpc_send (msock,
		  RPC_STRING, buf,
		  RPC_END);
    }
}

static long dev_to_long (dev_t dev)
{
    long mylong;
    int endian = 1;
    unsigned char *e;
    e = (unsigned char *) &endian;
    if (*e)
	memcpy (&mylong, &dev, sizeof (mylong));
    else
	memcpy (&mylong, (char *) &dev + sizeof (dev) - sizeof (mylong), sizeof (mylong));
    return mylong;
}

static dev_t long_to_dev (long mylong)
{
    dev_t dev;
    int endian = 1;
    unsigned char *e;
    e = (unsigned char *) &endian;
    memset (&dev, 0, sizeof (dev));
    if (*e)
	memcpy (&dev, &mylong, sizeof (mylong));
    else
	memcpy (&dev + sizeof (dev) - sizeof (mylong), &mylong, sizeof (mylong));
    return dev;
}

void send_stat_info (struct stat *st)
{
    int mylong;
    int blocks =
#ifdef HAVE_ST_BLOCKS
	st->st_blocks;
#else
        st->st_size / 1024;
#endif

#ifdef HAVE_ST_RDEV
/* FIXED */
    mylong = dev_to_long (st->st_rdev);
#else
    mylong = 0;
#endif
    rpc_send (msock, RPC_INT, (long) mylong, 
	      RPC_INT, (long) st->st_ino,
	      RPC_INT, (long) st->st_mode,
	      RPC_INT, (long) st->st_nlink,
	      RPC_INT, (long) st->st_uid,
	      RPC_INT, (long) st->st_gid,
	      RPC_INT, (long) st->st_size,
	      RPC_INT, (long) blocks, RPC_END);
    send_time (msock, st->st_atime);
    send_time (msock, st->st_mtime);
    send_time (msock, st->st_ctime);
}

void do_lstat (void)
{
    struct stat st;
    char   *file;
    int    n;

    rpc_get (msock, RPC_STRING, &file, RPC_END);
    n = lstat (file, &st);
    send_status (n, errno);
    if (n >= 0)
	send_stat_info (&st);
    free (file);
}

void do_fstat (void)
{
    int handle;
    int n;
    struct stat st;
   
    rpc_get (msock, RPC_INT, &handle, RPC_END);
    n = fstat (handle, &st);
    send_status (n, errno);
    if (n < 0)
	return;

    send_stat_info (&st);
}

void do_stat (void)
{
    struct stat st;
    int    n;
    char   *file;

    rpc_get (msock, RPC_STRING, &file, RPC_END);

    n = stat (file, &st);
    send_status (n, errno);
    if (n >= 0)
	send_stat_info (&st);
    free (file);
}

/* }}} */

/* {{{ Directory lookup operations */

static struct {
    int used;
    DIR *dirs [OPENDIR_HANDLES];
    char *names [OPENDIR_HANDLES];
} mcfs_DIR;

void close_handle (int handle)
{
    if (mcfs_DIR.used > 0) mcfs_DIR.used--;
    if (mcfs_DIR.dirs [handle])
	closedir (mcfs_DIR.dirs [handle]);
    if (mcfs_DIR.names [handle])
	free (mcfs_DIR.names [handle]);
    mcfs_DIR.dirs [handle] = 0;
    mcfs_DIR.names [handle] = 0;
}

void do_opendir (void)
{
    int handle, i;
    char *arg;
    DIR *p;

    rpc_get (msock, RPC_STRING, &arg, RPC_END);

    if (mcfs_DIR.used == OPENDIR_HANDLES){
	send_status (-1, ENFILE);	/* Error */
	free (arg);
	return;
    }
    
    handle = -1;
    for (i = 0; i < OPENDIR_HANDLES; i++){
	if (mcfs_DIR.dirs [i] == 0){
	    handle = i;
	    break;
	}
    }

    if (handle == -1){
	send_status (-1, EMFILE);
	free (arg);
	if (!inetd_started)
	    fprintf (stderr, "OOPS! you have found a bug in mc - do_opendir()!\n");
	return;
    }

    if (verbose) printf ("handle=%d\n", handle);
    p = opendir (arg);
    if (p){
	mcfs_DIR.dirs [handle] = p;
	mcfs_DIR.names [handle] = arg;
	    mcfs_DIR.used ++;

	/* Because 0 is an error value */
	rpc_send (msock, RPC_INT, handle+1, RPC_INT, 0, RPC_END);

    } else {
	send_status (-1, errno);
	free (arg);
    }
}

/* Sends the complete directory listing, as well as the stat information */
void do_readdir (void)
{
    struct dirent *dirent;
    struct stat st;
    int    handle, n, dnamelen;
    char   *fname = 0;

    rpc_get (msock, RPC_INT, &handle, RPC_END);
    
    if (!handle){
	rpc_send (msock, RPC_INT, 0, RPC_END);
	return;
    }

    /* We incremented it in opendir */
    handle --;
    dnamelen = strlen (mcfs_DIR.names [handle]);

    while ((dirent = readdir (mcfs_DIR.dirs [handle]))){
	int length = NAMLEN (dirent);

	rpc_send (msock, RPC_INT, length, RPC_END);
	rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
	fname = malloc (dnamelen + length + 2);
	strcat (strcat (strcpy (fname, mcfs_DIR.names [handle]), PATH_SEP_STR), dirent->d_name);
	n = lstat (fname, &st);
	send_status (n, errno);
	free (fname);
	if (n >= 0)
	    send_stat_info (&st);
    }
    rpc_send (msock, RPC_INT, 0, RPC_END);
}

void do_closedir (void)
{
    int handle;

    rpc_get (msock, RPC_INT, &handle, RPC_END);
    close_handle (handle-1);
}

/* }}} */

/* {{{ Operations with one and two file name argument */

void do_chdir (void)
{
    char *file;
    int  status;

    rpc_get (msock, RPC_STRING, &file, RPC_END);
    
    status = chdir (file);
    send_status (status, errno);
    free (file);
}

void do_rmdir (void)
{
    char *file;
    int  status;

    rpc_get (msock, RPC_STRING, &file, RPC_END);
    
    status = rmdir (file);
    send_status (status, errno);
    free (file);
}

void do_mkdir (void)
{
    char *file;
    int  mode, status;

    rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
    
    status = mkdir (file, mode);
    send_status (status, errno);
    free (file);
}

void do_mknod (void)
{
    char *file;
    umode_t smode;
    dev_t sdev;
    int  mode, dev, status;

    rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev, RPC_END);
    sdev = long_to_dev (dev);
    smode = mode;

    status = mknod (file, smode, sdev);
    send_status (status, errno);
    free (file);
}

void do_readlink (void)
{
    char buf [4096];
    char *file;
    int  n;

    rpc_get (msock, RPC_STRING, &file, RPC_END);
    n = readlink (file, buf, 4096);
    send_status (n, errno);
    if (n >= 0) {
        buf [n] = 0;
	rpc_send (msock, RPC_STRING, buf, RPC_END);
    }
    free (file);
}

void do_unlink (void)
{
    char *file;
    int  status;
    
    rpc_get (msock, RPC_STRING, &file, RPC_END);
    status = unlink (file);
    send_status (status, errno);
    free (file);
}

void do_rename (void)
{
    char *f1, *f2;
    int  status;
    
    rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
    status = rename (f1, f2);
    send_status (status, errno);
    free (f1); free (f2);
}

void do_symlink (void)
{
    char *f1, *f2;
    int  status;
    
    rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
    status = symlink (f1, f2);
    send_status (status, errno);
    free (f1); free (f2);
}

void do_link (void)
{
    char *f1, *f2;
    int  status;
    
    rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
    status = link (f1, f2);
    send_status (link (f1, f2), errno);
    free (f1); free (f2);
}


/* }}} */

/* {{{ Misc commands */

void do_closelast (void)
{
    quick_file_read = 1;
}

void do_gethome (void)
{
    rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : PATH_SEP_STR, RPC_END);
}

void do_getupdir (void)
{
    rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : PATH_SEP_STR, RPC_END);
}

void do_chmod (void)
{
    char *file;
    int  mode, status;
    
    rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
    status = chmod (file, mode);
    send_status (status, errno);
    free (file);
}

void do_chown (void)
{
    char *file;
    int  owner, group, status;
    
    rpc_get (msock, RPC_STRING, &file,RPC_INT, &owner, RPC_INT,&group,RPC_END);
    status = chown (file, owner, group);
    send_status (status, errno);
    free (file);
}

void do_utime (void)
{
    char *file;
    int  status;
    long atime;
    long mtime;
    char *as;
    char *ms;
    struct utimbuf times;
    
    rpc_get (msock, RPC_STRING, &file,
		    RPC_STRING, &as,
		    RPC_STRING, &ms,
		    RPC_END);
    sscanf (as, "%lx", &atime);
    sscanf (ms, "%lx", &mtime);
    if (verbose) printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n",
			  as, ms, atime, mtime);
    free (as);
    free (ms);
    times.actime  = (time_t) atime;
    times.modtime = (time_t) mtime;
    status = utime (file, &times);
    send_status (status, errno);
    free (file);
}

void do_quit (void)
{
    quit_server = 1;
}

#ifdef HAVE_PAM

struct user_pass {
    char *username;
    char *password;
};

int
mc_pam_conversation (int messages, const struct pam_message **msg,
		     struct pam_response **resp, void *appdata_ptr)
{
    struct pam_response *r;
    struct user_pass *up = appdata_ptr;
    int status;

    r = (struct pam_response *) malloc (sizeof (struct pam_response) * messages);
    if (!r)
	return PAM_CONV_ERR;
    *resp = r;
    
    for (status = PAM_SUCCESS; messages--; msg++, r++){
	switch ((*msg)->msg_style){

	case PAM_PROMPT_ECHO_ON: 
	    r->resp = (char *) strdup (up->username);
	    r->resp_retcode  = PAM_SUCCESS;
	    break;

	case PAM_PROMPT_ECHO_OFF:
	    r->resp = (char *) strdup (up->password);
	    r->resp_retcode = PAM_SUCCESS;
	    break;

	case PAM_ERROR_MSG:
	    r->resp = NULL;
	    r->resp_retcode = PAM_SUCCESS;
	    break;

	case PAM_TEXT_INFO:
	    r->resp = NULL;
	    r->resp_retcode = PAM_SUCCESS;
	    break;
	}
    }
    return status;
}

static struct pam_conv conv = { &mc_pam_conversation, NULL };
			     

/* Return 0 if authentication failed, 1 otherwise */
int
mc_pam_auth (char *username, char *password)
{
    pam_handle_t *pamh;
    struct user_pass up;
    int status;
    
    up.username = username;
    up.password = password;
    conv.appdata_ptr = &up;
    
    if ((status = pam_start("secure-mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
	goto failed_pam;
    if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
	goto failed_pam;
    if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
	goto failed_pam;
    if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
	goto failed_pam;
    pam_end (pamh, status);
    return 0;
    
failed_pam:
    if (verbose)
	printf ("%s\n", pam_strerror (pamh, status));
    pam_end (pamh, status);
    return 1;
}

#else /* Code for non-PAM authentication */
    
/* Keep reading until we find a \n */
static int next_line (int sock)
{
    char c;

    while (1){
	if (read (sock, &c, 1) <= 0)
	    return 0;
	if (c == '\n')
	    return 1;
    }
    return 0;
}

static int ftp_answer (int sock, char *text)
{
    char answer [4];

    next_line (sock);
    socket_read_block (sock, answer, 3);
    answer [3] = 0;
    if (strcmp (answer, text) == 0)
	return 1;
    return 0;
}

int do_ftp_auth (char *username, char *password)
{
    struct   sockaddr_in local_address;
    unsigned long inaddr;
    int      my_socket;
    char     answer [4];

    bzero ((char *) &local_address, sizeof (local_address));
    
    local_address.sin_family = AF_INET;
    /* FIXME: extract the ftp port with the proper function */
    local_address.sin_port   = htons (21);

    /*  Convert localhost to usable format */
    if ((inaddr = inet_addr ("127.0.0.1")) != -1)
	bcopy ((char *) &inaddr, (char *) &local_address.sin_addr,
	       sizeof (inaddr));
    
    if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0){
	if (!isDaemon) fprintf (stderr, "do_auth: can't create socket\n");
	return 0;
    }
    if (connect (my_socket, (struct sockaddr *) &local_address,
	     sizeof (local_address)) < 0){
	fprintf (stderr,
		 "do_auth: can't connect to ftp daemon for authentication\n");
	close (my_socket);
	return 0;
    }
    send_string (my_socket, "user ");
    send_string (my_socket, username);
    send_string (my_socket, "\r\n");
    if (!ftp_answer (my_socket, "331")){
	send_string (my_socket, "quit\r\n");
	close (my_socket);
	return 0;
    }
    next_line (my_socket);	/* Eat all the line */
    send_string (my_socket, "pass ");
    send_string (my_socket, password);
    send_string (my_socket, "\r\n");
    socket_read_block (my_socket, answer, 3);
    answer [3] = 0;
    send_string (my_socket, "\r\n");
    send_string (my_socket, "quit\r\n");
    close (my_socket);
    if (strcmp (answer, "230") == 0)
	return 1;
    return 0;
}

/* is this necessary? -paul */
#if 0
#ifdef NEED_CRYPT_PROTOTYPE
    extern char *crypt (const char *, const char *);
#endif
#endif

int do_classic_auth (char *username, char *password)
{
    struct passwd *this;
    int ret = 0;

    if (!(this = getpwnam (username)))
	return 0;

#if defined(HAVE_SHADOW)
    {
	struct spwd *spw;
	if (!(spw = (struct spwd *) getspnam (username)))
	    this->pw_passwd = "*";
	else
	    this->pw_passwd = spw->sp_pwdp;
#   if defined(HAVE_PW_ENCRYPT)
	if (!strcmp ((char *) pw_encrypt ((char *) password, (char *) this->pw_passwd), (char *) this->pw_passwd))
	    ret = 1;
#   elif defined(HAVE_CRYPT)
	if (!strcmp ((char *) crypt ((char *) password, (char *) this->pw_passwd), (char *) this->pw_passwd))
	    ret = 1;
#   else
#      error Unsupported shadow passwords. Please contact mirrordir@mail.obsidian.co.za
#   endif
    }
#elif defined(HAVE_CRYPT)
    if (!strcmp ((char *) crypt ((char *) password, (char *) this->pw_passwd), (char *) this->pw_passwd))
	ret = 1;
#else
#   error crypt() not found on your system. Please contact mirrordir@mail.obsidian.co.za
#endif
    endpwent ();
    return ret;
}


#endif				/* non-PAM authentication */

/* Try to authenticate the user based on:
   - PAM if the system has it, else it checks:
   - pwdauth if the system supports it.
   - conventional auth (check salt on /etc/passwd, crypt, and compare
   - try to contact the local ftp server and login (if -f flag used)
*/
int
do_auth (char *username, char *password, int set_user)
{
    int auth = 0;
    struct passwd *this;
    
    if (strcmp (username, "anonymous") == 0)
	username = "ftp";

#ifdef HAVE_PAM
    if (mc_pam_auth (username, password) == 0)
	auth = 1;
#else /* if there is no pam */
#ifdef HAVE_PWDAUTH
    if (pwdauth (username, password) == 0)
	auth = 1;
    else
#endif
    if (do_classic_auth (username, password))
	auth = 1;
    else if (ftp)
	auth = do_ftp_auth (username, password);
#endif /* not pam */
    
    if (!auth)
        return 0;
	
    this = getpwnam (username);
    if (this == 0)
	return 0;

    if (chdir (this->pw_dir) == -1)
        return 0;
    
    if (this->pw_dir [strlen (this->pw_dir) - 1] == PATH_SEP)
        home_dir = (char *) strdup (this->pw_dir);
    else {
        home_dir = malloc (strlen (this->pw_dir) + 2);
        if (home_dir) {
            strcpy (home_dir, this->pw_dir);
            strcat (home_dir, PATH_SEP_STR);
        } else
            home_dir = PATH_SEP_STR;
    }
    	
    
    if (setgid (this->pw_gid) == -1)
        return 0;
    
#ifdef HAVE_INITGROUPS
#ifdef NGROUPS_MAX
    if (NGROUPS_MAX > 1 && initgroups ((const char *) this->pw_name, (gid_t) this->pw_gid))
        return 0;
#endif
#endif    

    if (set_user) {
#ifdef HAVE_SETREUID
	if (setreuid (this->pw_uid, this->pw_uid))
#else
	if (setuid (this->pw_uid))
#endif
	    return 0;
    /* If the setuid call failed, then deny access */
    /* This should fix the problem on those machines with strange setups */
	if (getuid () != this->pw_uid)
	    return 0;
    }

    if (strcmp (username, "ftp") == 0)
	chroot (this->pw_dir);

    endpwent ();
    return auth;
}

#if 0
int do_rauth (int socket)
{
    struct sockaddr_in from;
    struct hostent *hp;
    
    if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0)
	return 0;
    from.sin_port = ntohs ((unsigned short) from.sin_port);

    /* Strange, this should not happend */
    if (from.sin_family != AF_INET)
	return 0;

    hp = gethostbyaddr((char *)&fromp.sin_addr, sizeof (struct in_addr),
		       fromp.sin_family);
    
}
#endif

int do_rauth (int sock)
{
    return 0;
}

void login_reply (int is_logged_in)
{
    rpc_send (msock, RPC_INT,
	      is_logged_in ? MC_LOGINOK : MC_INVALID_PASS,
	      RPC_END);
}

/* FIXME: Implement the anonymous login */
void xdo_login (int set_user)
{
    char *username;
    char *password;
    int result;

    rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING, &username, RPC_END);
    strncpy (client_user, username, 60);

    if (verbose)
	printf ("username: %s\n", username);

    rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
    rpc_get (msock, RPC_INT, &result, RPC_END);
    if (result == MC_QUIT)
	DO_QUIT_VOID ();
    if (result != MC_PASS) {
	if (verbose)
	    printf ("do_login: Unknown response: %d\n", result);
	DO_QUIT_VOID ();
    }
    rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
    if (password_server) {
	char s[256];
	FILE *p;
	sprintf (s, "%s %s@%s --test-login --read-password-from-stdin --no-warn-first-login", AUTH_PROG, username, password_server);
	p = popen (s, "w");
	if (!p) {
	    printf ("do_login: popen failed\n");
	} else {
	    fwrite (password, strlen (password), 1, p);
	    logged_in = !pclose (p);
	}
    } else {
	logged_in = do_auth (username, password, set_user);
    }
    endpwent ();
    login_reply (logged_in);
}

void do_login (void)
{
    xdo_login (1);
}

void xdo_shell (void);

void do_shell (void)
{
    xdo_login (0);
    if (!logged_in)
	exit (1);
    xdo_shell ();
    exit (1);
}

extern char **environ;
static char *new_environ[2];
static char line[4096];
FILE *my_stdout;
pid_t login_pid;

pid_t forkpty(int *, char *, struct termios *, struct winsize *);

#ifndef FD_SETSIZE
#define FD_SETSIZE 128
#endif

/* our protocol is simple: 0-0xFF: actual data 0x100-0xFFFFFFFF: commands */
enum {
    SHELL_WINSZ = 256,
    SHELL_DATA
/* other commands my follow */
};

/* #define shell_debug(x,y) fprintf(my_stdout,x,y) */
#define shell_debug(x,y)

#if 0
#define debug(x,y,d) fprintf (my_stdout, "%s: %s: %d\n", (x), (y), (d))
#else
#define debug(x,y,d) 
#endif

#undef MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#undef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define WOULD_BLOCK(c) { if ((c) < 0 && errno == EWOULDBLOCK) (c) = 0; if ((c) < 0) break; }

#undef BUFFER_SIZE
#define BUFFER_SIZE 1024

void tcp_reroute_data (int my_socket, int p)
{
    int remaining_in = 0;
    char remote_buf[BUFFER_SIZE + 1], local_buf[BUFFER_SIZE + 1];
    int remote_avail = 0, local_avail = 0;
    int remote_written = 0, local_written = 0;
    for (;;) {
	int cmd, r;
	int n = 0;
	fd_set writing, reading;
	FD_ZERO (&writing);
	FD_ZERO (&reading);
/* is there stuff waiting to be written to local ? */
	if (remote_avail > remote_written) {
	    FD_SET (p, &writing);
	    n = MAX (p, n);
	}
/* is there stuff waiting to be written to remote ? */
	if (local_avail > local_written) {
	    FD_SET (my_socket, &writing);
	    n = MAX (my_socket, n);
	}
/* is there buffer space to read from local ? */
	if (local_avail < BUFFER_SIZE) {
	    FD_SET (p, &reading);
	    n = MAX (p, n);
	}
/* is there buffer space to read from remote ? */
	if (remote_avail < BUFFER_SIZE) {
	    FD_SET (my_socket, &reading);
	    n = MAX (my_socket, n);
	}
	debug ("select", "", n);
	if (idle_allow) {
	    struct timeval tv;
	    tv.tv_sec = idle_allow;
	    tv.tv_usec = 0;
	    cmd = SHELL_DATA;
	    do {
		r = select (n + 1, &reading, &writing, NULL, &tv);
	    } while (r == -1 && errno == EINTR);
	    if (r == -1)
		break;
	    if (!r) {
		char msg[256];
		int msg_len;
		sprintf (msg, "secure-mcserv: login idle for %d seconds - kicking you off\r\n%n", idle_allow, &msg_len);
		rpc_send (my_socket, RPC_INT, cmd, RPC_INT, msg_len, RPC_BLOCK, msg_len, msg, RPC_END);
		break;
	    }
	} else {
	    do {
		r = select (n + 1, &reading, &writing, NULL, NULL);
	    } while (r == -1 && errno == EINTR);
	    if (r == -1)
		break;
	}
	debug ("select", "done", 0);
	if (FD_ISSET (p, &reading)) {
	    int c;
	    debug ("reading", "0", BUFFER_SIZE - local_avail);
	    c = read (p, local_buf + local_avail, BUFFER_SIZE - local_avail);
	    if (!c)
		break;
	    debug ("read   ", "0", c);
	    WOULD_BLOCK (c);
	    local_avail += c;
	    if (c)
		goto try_write_remote;
	}
	if (FD_ISSET (my_socket, &writing)) {
	    int c;
	  try_write_remote:
	    cmd = SHELL_DATA;
	    debug ("writing", "my_socket", local_avail - local_written);
	    c = local_avail - local_written;
	    if (c == 1) {
		if (!rpc_send (my_socket, RPC_INT, (int) ((unsigned char) local_buf[local_written]), RPC_END))
		    break;
	    } else if (c > 0) {
		if (!rpc_send (my_socket, RPC_INT, cmd, \
			       RPC_INT, c, RPC_BLOCK, c, \
			       local_buf + local_written, RPC_END))
		    break;
	    }
	    debug ("written", "my_socket", c);
	    local_written += c;
	    if (local_written == local_avail)
		local_avail = local_written = 0;
	}
	if (FD_ISSET (my_socket, &reading)) {
	    int c;
	    debug ("reading", "my_socket", BUFFER_SIZE - remote_avail);
	    if (!remaining_in) {
		if (!rpc_get (my_socket, RPC_INT, &cmd, RPC_END))
		    break;
		if (cmd >= 256 && cmd != SHELL_DATA && cmd != SHELL_WINSZ)	/* only support these commands so far */
		    break;
		if (cmd < 256) {
		    remote_buf[remote_avail] = (char) cmd;
		    remote_buf[remote_avail + 1] = '\0';
		    c = 1;
		} else if (cmd == SHELL_WINSZ) {
		    int row, col, xpixel, ypixel;
#ifdef TIOCGWINSZ
		    struct winsize w;
#endif
		    debug ("SHELL_WINSZ recieved", "", 0);
		    if (!rpc_get (my_socket, RPC_INT, &row, RPC_INT, &col, RPC_INT, &xpixel, RPC_INT, &ypixel, RPC_END))
			break;
#ifdef TIOCGWINSZ
		    w.ws_row = row;
		    w.ws_col = col;
		    w.ws_xpixel = xpixel;
		    w.ws_ypixel = ypixel;
		    ioctl (p, TIOCSWINSZ, &w);
#endif
		    goto done_check_socket_reading;
		} else {
		    if (!rpc_get (my_socket, RPC_INT, &remaining_in, RPC_END))
			break;
		    goto get_more_remaining;
		}
	    } else {
	      get_more_remaining:
		c = MIN (remaining_in, BUFFER_SIZE - remote_avail);
		if (!rpc_get (my_socket, RPC_BLOCK, c, remote_buf + remote_avail, RPC_END))
		    break;
		remote_buf[remote_avail + c] = '\0';
		remaining_in -= c;
	    }
	    if (!c)
		break;
	    debug ("read   ", "my_socket", c);
	    remote_avail += c;
	    if (c)
		goto try_write_local;
	}
      done_check_socket_reading:
	if (FD_ISSET (p, &writing)) {
	    int c;
	  try_write_local:
	    debug ("writing", "1", remote_avail - remote_written);
	    errno = 0;
	    c = write (p, remote_buf + remote_written, remote_avail - remote_written);
	    debug ("written", "1", c);
	    debug ("written", strerror (errno), errno);
	    WOULD_BLOCK (c);
	    remote_written += c;
	    if (remote_written == remote_avail)
		remote_avail = remote_written = 0;
	}
    }
}

void xdo_shell (void)
{
#ifdef HAVE_FORKPTY
    struct termios tt;
    char *terminal, *p, *path;
    int speed;
    int good_dup = 0;
    int master;
    struct winsize win;
    int log_fd;
    int yes = 1;

    rpc_get (msock, RPC_LIMITED_STRING, &terminal,
	     RPC_INT, &speed, RPC_LIMITED_STRING, &path, RPC_END);

    if ((log_fd = dup (1)) <= 2)
	if (verbose)
	    printf ("dup failed\n");

    if (!(my_stdout = (FILE *) fdopen (log_fd, "w")))
	if (verbose)
	    printf ("fdopen for verbose messages failed\n");

    close (0);
    close (1);
    close (2);

    if (dup (msock) == 0)
	if (dup (msock) == 1)
	    if (dup (msock) == 2)
		good_dup = 1;
    if (!good_dup) {
	if (verbose)
	    fprintf (my_stdout, "dup failed\n");
	return;
    }
    memset (&win, 0, sizeof (win));
    login_pid = forkpty (&master, line, NULL, &win);
    if (login_pid < 0) {
	if (verbose)
	    fprintf (my_stdout, "fail getting a pty\n");
	return;
    }
    if (verbose)
	fprintf (my_stdout, "fork succeeded\n");

    if (!login_pid) {
/* set terminal speed */
	tcgetattr (0, &tt);
	if (speed) {
	    cfsetispeed (&tt, speed);
	    cfsetospeed (&tt, speed);
	}
	tcsetattr (0, TCSAFLUSH, &tt);

/* set TERM environment variable */
	p = malloc (strlen (terminal) + 6);
	strcpy (p, "TERM=");
	strcat (p, terminal);
	new_environ[0] = p;
	new_environ[1] = 0;

/* setup new environment */
	environ = new_environ;
	if (verbose)
	    fprintf (my_stdout, "executing login %s/%d\n", new_environ[0], speed);

/* execute login */
	execl ("/bin/login", "login", "-p",
	       "-h", strdup (inet_ntoa (client_addr)),
	       "-f", client_user, 0);

/* not reached */
	exit (1);
    };

/* reroute data on the other side of the fork... */

    signal (SIGCHLD, SIG_IGN);
    signal (SIGPIPE, SIG_IGN);

    if (verbose)
	fprintf (my_stdout, "rerouting streams\n");
    ioctl (master, FIONBIO, &yes);
    tcp_reroute_data (0, master);
    close (0);
    close (master);

    if (verbose)
	fprintf (my_stdout, "cleaning up\n");

#ifndef _PATH_DEV
#define _PATH_DEV "/dev/"
#endif

    p = line + sizeof (_PATH_DEV) - 1;
#ifdef HAVE_LOGOUT
    logout ((char *) p);	/* actually don't know what this is for, rlogin has it - paul */
#endif

    chmod (line, S_IWUSR | S_IRUSR);
    chown (line, 0, 0);
    *p = 'p';
    chmod (line, 0666);
    chown (line, 0, 0);
    if (verbose)
	fprintf (my_stdout, "shutting socket\n");
    close (msock);
    if (verbose)
	fprintf (my_stdout, "exitting\n");
#endif			/* HAVE_FORKPTY */
    exit (1);
}

void redirect (int local_sock, int remote_sock);

void do_redirect (void)
{
    char *redirect_to_machine;
    int redirect_to_port;
    int local_socket;
    struct sockaddr_in s;
    int yes = 1;
    int reserved_port;
    int client_port;

    if (verbose)
	printf ("redirect: getting machine\n");

    rpc_get (msock, RPC_STRING, &redirect_to_machine, RPC_INT, &redirect_to_port, RPC_INT, &client_port, RPC_END);

    if (verbose)
	printf ("redirect: machine=%s, port=%d\n", redirect_to_machine, redirect_to_port);

    memset (&s, 0, sizeof (s));
#ifdef HAVE_INET_ATON
    if (!inet_aton (redirect_to_machine, &s.sin_addr)) {
#else
    if ((s.sin_addr.s_addr = inet_addr(redirect_to_machine)) == -1) {
#endif
	struct hostent *h;
	h = gethostbyname (redirect_to_machine);
	if (!h) {
	    send_status (-1, errno);
	    return;
	}
	memcpy (&s.sin_addr, h->h_addr_list[0], h->h_length);
    }
    s.sin_family = AF_INET;
    s.sin_port = htons (redirect_to_port);

    if (verbose)
	printf ("redirect: getting socket()\n");
    if ((local_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
	if (verbose)
	    printf ("redirect: socket() failed\n");
	send_status (-1, errno);
	return;
    }
    if (verbose)
	printf ("client port is %d\n", client_port);
    if (client_port < IPPORT_RESERVED && client_port > IPPORT_RESERVED / 2 && !getuid ()) {
	if (verbose)
	    printf ("redirect: trying to get reserved port\n");
	for (reserved_port = IPPORT_RESERVED - 1; reserved_port > IPPORT_RESERVED / 2; reserved_port--) {
	    struct sockaddr_in sin;
	    memset (&sin, 0, sizeof (sin));
	    sin.sin_family = AF_INET;
	    sin.sin_addr.s_addr = INADDR_ANY;
	    sin.sin_port = htons ((unsigned short) reserved_port);
	    if (bind (local_socket, (struct sockaddr *) &sin, sizeof (sin)) >= 0) {
		if (verbose)
		    printf ("redirect: got reserved port\n");
		break;
	    }
	}
    }
    if (verbose)
	printf ("redirect: running connect()\n");
/* we undef cause we want an ordinary connect to an ordinary
   service - not a secure or gzipped connection: */
#undef connect
    if (connect (local_socket, (struct sockaddr *) &s, sizeof (s)) < 0) {
	send_status (-1, errno);
	close (local_socket);
	return;
    }
    send_status (0, errno);
    if (verbose)
	printf ("redirect: doing redirect()\n");
    ioctl (local_socket, FIONBIO, &yes);
    redirect (msock, local_socket);
    close (local_socket);
    quit_server = 1;
}

/* }}} */

/* {{{ Server and dispatching functions */

/* This structure must be kept in synch with mcfs.h enums */

struct _command {
    char *command;
    void (*callback)(void);
} commands [] = {
    { "open",       do_open },
    { "close",      do_close },
    { "read",       do_read },
    { "write",      do_write },
    { "opendir",    do_opendir }, 
    { "readdir",    do_readdir },
    { "closedir",   do_closedir },
    { "stat ",      do_stat },
    { "lstat ",     do_lstat },
    { "fstat",      do_fstat },
    { "chmod",      do_chmod },
    { "chown",      do_chown },
    { "readlink ",  do_readlink },
    { "unlink",     do_unlink },
    { "rename",     do_rename },
    { "chdir ",     do_chdir },
    { "lseek",      do_lseek },
    { "rmdir",      do_rmdir },
    { "symlink",    do_symlink },
    { "mknod",      do_mknod },
    { "mkdir",      do_mkdir },
    { "link",       do_link },
    { "gethome",    do_gethome },
    { "getupdir",   do_getupdir },
    { "login",      do_login },
    { "quit",       do_quit },
    { "utime",      do_utime },
    { "shell",      do_shell },
    { "closelast",  do_closelast },
    { "redirect",   do_redirect }
};

static int ncommands = sizeof (commands) / sizeof (struct _command);

void exec_command (int command)
{
    if (command < 0 ||
	command >= ncommands ||
	commands [command].command == 0){
	fprintf (stderr, "Got unknown command: %d\n", command);
	DO_QUIT_VOID ();
    }
    if (verbose) printf ("Command: %s\n", commands [command].command);
    (*commands [command].callback)();
}

void check_version (void)
{
    int version;
    
    rpc_get (msock, RPC_INT, &version, RPC_END);
    if (version >= 1 &&
	version <= RPC_PROGVER)
	rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
    else
	rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
    
    clnt_version = version;
}


void server (int sock)
{
    int command;
    
    msock = sock;
    quit_server = 0;

    check_version ();
    do {
	if (rpc_get (sock, RPC_INT, &command, RPC_END) &&
	    (logged_in || command == MC_LOGIN || command == MC_SHELL))
	    exec_command (command);
    } while (!quit_server);
}

/* }}} */

/* {{{ Net support code */

char *get_client (int port_number)
{
    int sock, clilen;
    struct sockaddr_in client_address, server_address;
    struct hostent *hp;
    char hostname[255];
    int yes = 1;
#ifdef HAVE_WRAP
    struct request_info curr_request;
#endif

    if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
	return "Can't create socket";

    /* Use this to debug: */
    if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
	return "setsockopt failed";

    gethostname (hostname, 255);
    if (verbose)
	printf ("hostname=%s\n", hostname);
    hp = gethostbyname (hostname);
    if (hp == 0)
	return "gethostbyname failed";

    bzero ((char *) &server_address, sizeof (server_address));
    server_address.sin_family = hp->h_addrtype;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons (port_number);

    if (bind (sock, (struct sockaddr *) &server_address,
	      sizeof (server_address)) < 0)
	return "Can't bind";

    listen (sock, 5);

    for (;;) {
	int child, newsocket;
#if defined(IP_TOS) && 0
	int on;
#endif
	signal (SIGPIPE, SIG_IGN);
	signal (SIGCHLD, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	clilen = sizeof (client_address);
#undef accept
	newsocket = accept (sock, (struct sockaddr *) &client_address,
			    (unsigned int *) &clilen);
	if (newsocket < 0)
	    continue;
	if (isDaemon && (child = fork ())) {
	    int status;

	    close (newsocket);
	    waitpid ((pid_t) child, (int *) &status, (int) 0);
	    continue;
	}
	if (isDaemon && fork ())
	    exit (0);
#ifdef HAVE_WRAP
	request_init (&curr_request, RQ_DAEMON, "secure-mcserv",
		 RQ_CLIENT_ADDR, inet_ntoa (client_address.sin_addr), 0);
	if (!hosts_access (&curr_request)) {
	    close (newsocket);
	    return 0;
	}
#endif
#if defined(IP_TOS) && 0
	on = IPTOS_LOWDELAY;
	setsockopt (newsocket, IPPROTO_IP, IP_TOS, (char *) &on, sizeof (int));
#endif
	newsocket = arc_socket_accept_fd (newsocket);
	if (newsocket < 0)
	    return 0;
	newsocket = z_socket_accept_fd (newsocket);
	if (newsocket < 0)
	    return 0;
	memcpy (&client_addr, &client_address.sin_addr, sizeof (client_addr));
	server (newsocket);
	close (newsocket);
	my_pid = getpid ();
	return 0;
    }
}

#ifdef HAVE_PMAP_SET
void signal_int_handler (int sig)
{
    pmap_unset (RPC_PROGNUM, RPC_PROGVER);
}
#endif

#ifndef IPPORT_RESERVED
#define IPPORT_RESERVED 1024;
#endif

int get_port_number (void)
{
    int port = 0;

#ifdef HAVE_RRESVPORT
    int start_port = IPPORT_RESERVED;
    
    port = rresvport (&start_port);
    if (port == -1){
	if (geteuid () == 0){
	    fprintf (stderr, "Could not bind the server on a reserved port\n");
	    DO_QUIT_NONVOID (-1);
	}
	port = 0;
    }
#endif
    if (port)
	return port;
    
    port = mcserver_port;

    return port;
}

void register_port (int port_number, int abort_if_fail)
{
#ifdef HAVE_PMAP_SET
    /* Register our service with the portmapper */
    /* protocol: pmap_set (prognum, versnum, protocol, portp) */
    
    if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, port_number))
	signal (SIGINT, signal_int_handler);
    else {
	fprintf (stderr, "Could not register service with portmapper\n");
	if (abort_if_fail)
	    exit (1);
    }
#else
    if (abort_if_fail){
	fprintf (stderr,
		 "This system lacks port registration, try using the -p\n"
		 "flag to force installation at a given port");
    }
#endif
}

/* }}} */

extern void diffie_add_functions (void);

#define SCRIPT_ACCEPT_MIN_SIZE 80

void check_scripts (void)
{
    struct stat s;
    if (stat (SCRIPT_ACCEPT, &s)) {
	fprintf (stderr, "Cannot locate script " SCRIPT_ACCEPT ": Mirrordir has not been installed properly");
	exit (1);
    }
    if (s.st_size < SCRIPT_ACCEPT_MIN_SIZE) {
	fprintf (stderr, "Secure-mcserv has detected that " SCRIPT_ACCEPT " is truncated.\n");
	fprintf (stderr, "This means that you have a US version of this software.\n");
	fprintf (stderr, "run\n\tmirrordir --download-scripts\nto download scripts first.\n");
	exit (1);
    }
}

int main (int argc, char *argv [])
{
    char *result;
    extern char *optarg;
    int c;

    check_scripts ();
    diffie_add_functions ();
    parser_init ();

    while ((c = getopt (argc, argv, "fdiqs:p:t:v")) != -1){
	switch (c){
	case 'd':
	    isDaemon = 1;
	    verbose = 0;
	    break;

	case 'v':
	    verbose = 1;
	    break;
	    
	case 't':
	    idle_allow = atoi (optarg);
	    break;
	    
	case 'f':
	    ftp = 1;
	    break;

	case 'q':
	    verbose = 0;
	    break;

	case 'p':
	    portnum = atoi (optarg);
	    break;

	case 'i':
	    inetd_started = 1;
	    break;

	case 'r':
	    r_auth = 1;
	    break;

	case 's':
	    password_server = (char *) strdup (optarg);
	    break;

	default:
	    fprintf (stderr, 
		    "secure-mcserv from Mirrordir version " VERSION " "
#ifdef HAVE_BUILTIN_ARC
		    "(International - builtin encryption)\n"
#else
		    "(US - slow script encryption)\n"
#endif
		    "Usage is: secure-mcserv [options] [-p portnum] [-t seconds]\n\n"
		    "options are:\n"
		    "-d  become a daemon (sets -q)\n"
		    "-q  quiet mode\n"
		/*    "-r  use rhost based authentication\n" */
#ifndef HAVE_PAM
		    "-f  force ftp authentication\n"
#endif
		    "-v  verbose mode\n"
		    "-t  specify the number of idle seconds before a shell login\n"
		    "    is timed out\n"
		    "-p  to specify a port number to listen\n");
	    exit (0);
	    
	}
    }

    if (isDaemon && fork()) exit (0);

    if (portnum == 0)
	portnum = get_port_number ();

    if (portnum != -1) {
	register_port (portnum, 0);
	if (verbose)
	    printf ("Using port %d\n", portnum);
	if ((result = get_client (portnum)))
	    perror (result);
#ifdef HAVE_PMAP_SET
	if (!isDaemon)
	    pmap_unset (RPC_PROGNUM, RPC_PROGVER);
#endif
    }
    exit (return_code);
    return 0;	/* prevents warning */
}

/* This functions are not used */
void message (int is_error, char *text, char *msg)
{
    printf ("%s %s\n", text, msg);
}

char *unix_error_string (int a)
{
    return "none";
}

#ifndef HAVE_MAD
void *do_xmalloc (int size)
{
    void *m = malloc (size);

    if (!m){
	fprintf (stderr, "memory exhausted\n");
	exit (1);
    }
    return m;
}
#endif

#ifndef HAVE_STRDUP
#define strdup(x) my_strdup(x)
char *my_strdup (char *s)
{
    char *t = malloc (strlen (s)+1);
    strcpy (t, s);
    return t;
}
#endif

void vfs_die( char *m )
{
    fprintf (stderr,m);
    exit (1);
}

#else				/* ! OUTPUT_MESSAGE */

void tcp_invalidate_socket (int sock)
{
}

int main (int argc, char **argv)
{
    fprintf (stderr, "secure-mcserv: %s\n", OUTPUT_MESSAGE);
    exit (1);
    return 1;
}


#endif

