/* 
	portions Copyright (C) 2007 Alex deVries
	
*/

#include "afpfs-ng/afp.h"
#include "afpfs-ng/midlevel.h"
#include "afpfs-ng/map_def.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <ctype.h>

#include "cmdline_main.h"

static char curdir[AFP_MAX_PATH];
static struct afp_url url;

int full_url=0;

#define DEFAULT_DIRECTORY "/"

static struct afp_server * server = NULL;
struct afp_volume * vol= NULL;

static int recursive_get(char * path);

static int escape_paths(char * outgoing1, char * outgoing2, char * incoming)
{
	char * writeto=outgoing1;
	int inquote=0, inescape=0, donewith1=0;
	char *p = incoming;

	if ((outgoing1==NULL) || (strlen(incoming)==0)) {
		goto error;
	}

	memset(outgoing1,0,AFP_MAX_PATH);
	if (outgoing2) memset(outgoing2,0,AFP_MAX_PATH);
	
	for  (p=incoming;p<incoming+strlen(incoming);p++) {
		if (*p=='\\') {
			if (inescape) {
				inescape=0;
				goto add;
			} else {
				inescape=1;
				continue;
			}
		}
		if (*p=='"') {
			inquote=1;
			continue;
		}
		if (inquote) {
			if (*p=='"') {
				inquote=0;
				goto add;
			}
		} else {
			if (*p==' ') {
				if (inescape) 
					goto add;

				if ((donewith1==1)||(outgoing2==NULL)) 
					goto out;
				writeto=outgoing2;
				donewith1=1;
			}
		}
add:
		*writeto=*p;
		writeto++;
	}
out:
	if ((outgoing2!=NULL) && (donewith1==0)) 
		goto error;
	return 0;
error:
	return -1;
}

static unsigned int tvdiff(struct timeval * starttv, struct timeval * endtv)
{
	unsigned int d;
	d=(endtv->tv_sec - starttv->tv_sec) * 1000;
	d+= (endtv->tv_usec - starttv->tv_usec) / 1000;
	return d;
	
}

static void printdiff(struct timeval * starttv, struct timeval *endtv, 
	unsigned long long * amount_written)
{
	unsigned int diff;
	unsigned long long kb_written;

	diff=tvdiff(starttv,endtv);
	float frac = ((float) diff) / 1000.0; /* Now in seconds */
	printf("Transferred %lld bytes in ",*amount_written);
	printf("%.3f seconds. ",frac);

	/* Now calculate the transfer rate */
	kb_written = (*amount_written/1000);
	float rate = (kb_written)/frac;
	printf("(%.0f kB/s)\n", rate);
}

static int cmdline_getpass(void)
{
	char * passwd;
	if (strcmp(url.password,"-")==0) {
		passwd=getpass("Password:");
		strncpy(url.password,passwd,AFP_MAX_PASSWORD_LEN);
	}
	return 0;
}


static int get_server_path(char * filename,char * server_fullname)
{
	if (filename[0]!='/') {
		if (strlen(curdir)==1) 
			snprintf(server_fullname,PATH_MAX,"/%s",filename);
		else
			snprintf(server_fullname,PATH_MAX,"%s/%s",curdir,filename);
	} else {
		snprintf(server_fullname,PATH_MAX,"%s",filename);
	}
	return 0;
}

static void print_file_details(struct afp_file_info * p)
{
	struct tm * mtime;
	time_t t,t2;
#define DATE_LEN 32
	char datestr[DATE_LEN];
	char mode_str[11];
	uint32_t mode;
	struct stat stat;

	afp_unixpriv_to_stat(p,&stat);
	mode=stat.st_mode;

	sprintf(mode_str,"----------");

	t2=time(NULL);
	t=p->modification_date;
	mtime=localtime(&t);

	if (p->isdir) mode_str[0]='d';

	if (mode & S_IRUSR) mode_str[1]='r';
	if (mode & S_IWUSR) mode_str[2]='w';
	if (mode & S_IXUSR) mode_str[3]='x';
	
	if (mode & S_IRGRP) mode_str[4]='r';
	if (mode & S_IWGRP) mode_str[5]='w';
	if (mode & S_IXGRP) mode_str[6]='x';
	
	if (mode & S_IROTH) mode_str[7]='r';
	if (mode & S_IWOTH) mode_str[8]='w';
	if (mode & S_IXOTH) mode_str[9]='x';
	

	strftime(datestr,DATE_LEN,"%F %H:%M", mtime);

	printf("%s %6lld %s %s\n",mode_str,p->size,datestr,p->name);

}

static int connect_volume(const char * volumename) 
{

	if (strlen(volumename)==0) goto error;

	/* Ah, we're not connected to a volume*/
	unsigned int len=0;
	char mesg[1024];

	if ((vol = find_volume_by_name(server,volumename))==NULL) 
	{
		printf("Could not find a volume called %s\n",volumename);
		goto error;
	}
	vol->mapping= AFP_MAPPING_LOGINIDS;
	vol->extra_flags |= VOLUME_EXTRA_FLAGS_NO_LOCKING;

	if (afp_connect_volume(vol,server,mesg,&len,1024 ))
	{
		printf("Could not access volume %s\n",
			vol->volume_name);
		goto error;
	}

	printf("Connected to volume %s\n",vol->volume_name_printable);

	return 0;
error:
	return -1;
}

static int server_subconnect(void) 
{
	struct afp_connection_request * conn_req;

#define BUFFER_SIZE 2048
	conn_req = malloc(sizeof(struct afp_connection_request));
	if(!conn_req)
		return -1;

    memset(conn_req, 0,sizeof(struct afp_connection_request));

    conn_req->url=url;
	conn_req->url.requested_version=31;

	if (strlen(url.uamname)>0) {
		if ((conn_req->uam_mask = find_uam_by_name(url.uamname))==0) {
			printf("I don't know about UAM %s\n",url.uamname);
			return -1;
		}
		
	} else {
        	conn_req->uam_mask=default_uams_mask();
	}
	if ((server=afp_server_full_connect(NULL, conn_req))==NULL) {
		goto error;
	}

	printf("Connected to server %s using UAM \"%s\"\n",
		server->server_name_printable, uam_bitmap_to_string(server->using_uam));

	free(conn_req);

	return 0;
error:
	free(conn_req);
	printf("Could not connect\n");
	return -1;
}

int com_pass(char * arg)
{
	if ((strlen(arg)==0) || (strcmp(arg,"-"))) {
		getpass("Password: ");
		return -1;
	}
	printf("Password set.\n");

	strncpy(url.password,arg,AFP_MAX_PASSWORD_LEN);
	return 0;

}


int com_user(char * arg)
{
	if (strlen(arg)==0) {
		printf("You must specify a user\n");
	return -1;
	}

	strncpy(url.username,arg,AFP_MAX_PASSWORD_LEN);
	printf("username is now %s\n",url.username);
	return 0;
}

int com_disconnect(char * arg)
{
	if (server==NULL) {
		printf("You're not connected yet to a server\n");
		goto error;
	}
	afp_unmount_volume(vol);

	vol=NULL;

	server=NULL;
	snprintf(curdir,AFP_MAX_PATH,"/");

	return 0;
error:
	return -1;
}


int com_connect(char * arg)
{
	struct afp_url tmpurl;
	if (!arg)
		arg = "";

	if (server) {
		printf("You're already connected to a server\n");
		goto error;
	}

	afp_default_url(&tmpurl);

	/* First, try to parse the URL */
	
	if (afp_parse_url(&tmpurl,arg,0)!=0) {
		/* Okay, this isn't a real URL */
		printf("Could not parse url, let me see if this is a server name...\n");
		if (gethostbyname(arg)) 
			memcpy(&url.servername,arg,AFP_SERVER_NAME_LEN);
		else {
			printf("Cannot understand server name or url %s\n",arg);
			return -1;
		}
	} else {
		url=tmpurl;

	}
	cmdline_getpass();


	if (server_subconnect()) {
		printf("Could not connect\n");
		goto error;
	};

	connect_volume(url.volumename);

	return 0;
error:
	return -1;

}


int com_dir(char * arg)
{
	if (!arg)
		arg = "";

	struct afp_file_info *filebase = NULL, *p;

	if (server==NULL) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (strlen(url.volumename)==0) {
		char names[1024];
		afp_list_volnames(server,names,1024);
		printf("You're not connected to a volume, choose from %s\n",
			names);
		goto out;
	}
	
	if (ml_readdir(vol,curdir,&filebase)) goto error;

	if (filebase==NULL) goto out;
	for (p=filebase;p;p=p->next) {
		print_file_details(p);
	}
	afp_ml_filebase_free(&filebase);

out:
	
	return 0;
error:
	return -1;
}


int com_touch(char * arg)
{
	char server_fullname[AFP_MAX_PATH];
	int ret;
	char filename[AFP_MAX_PATH];
	char * basename = filename;

	if (escape_paths(filename,NULL,arg)) {
		printf("Syntax: touch <newfile>\n");
		goto error;
	}

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	get_server_path(basename,server_fullname);

	ret=ml_creat(vol,server_fullname,0600);
	return 0;
error:
	return -1;

	return 0;
}

int com_chmod(char * arg)
{
	unsigned int mode;
	char basename[PATH_MAX];
	char server_fullname[AFP_MAX_PATH];
	char modestring[100];
	int ret;

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (escape_paths(modestring,basename,arg)) {
		printf("expecting format: chmod <privs> <filename>\n");
		goto error;
	}

	if (sscanf(modestring,"%o",&mode)!=1) {
		printf("Mode of %s isn't octal\n", modestring);
		goto error;
	}

	get_server_path(basename,server_fullname);

	printf("Changing mode of %s to %o\n",server_fullname,mode);
	ret=ml_chmod(vol,server_fullname,mode);
	return 0;
error:
	return -1;
}


int com_put(char *arg)
{
	int ret, amount_read;
	struct afp_file_info *fp;
	int offset=0;
#define PUT_BUFSIZE 102400
	char buf[PUT_BUFSIZE];
	int fd;
	char server_fullname[AFP_MAX_PATH];
	char * basename;
	uid_t uid;
	gid_t gid;
	struct stat localstat;
	unsigned long long amount_written=0;
	struct timeval starttv,endtv;
	char filename[AFP_MAX_PATH];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if ((escape_paths(filename,NULL,arg))) {
		printf("expecting format: put <filename>\n");
		goto error;
	}

	/* FIXME find basename */
	basename=filename;

	get_server_path(basename,server_fullname);

	/* FIXME need a better way to get server's uid/gid */

	uid=getuid();
	gid=getgid();

	if (stat(filename, &localstat)!=0) {
		perror("Opening local file");
	}

	fd = open(filename,O_RDONLY);

	if (fd<0) {
		perror("Opening local file");
		goto error;
	}

	gettimeofday(&starttv,NULL);

	ret = ml_open(vol,server_fullname,O_CREAT | O_RDWR,&fp);

	if (ret<0) {
		printf("Problem opening file %s on server\n",basename);
		goto out;
	}

	/* Now set various permissions */

	ret=ml_chmod(vol,server_fullname,localstat.st_mode);
	if (ret==-ENOSYS) printf("cannot change permissions\n");
	if (ret) {
		printf("Could not change permissions to 0%o\n",
			localstat.st_mode);
	}

	while (1) {
		amount_read=read(fd,buf,PUT_BUFSIZE);
		if (amount_read<0) {
			perror("Reading");
			goto out;
		}
		if (amount_read==0) goto out;
		ret=ml_write(vol,server_fullname,buf,amount_read,
			offset,fp,uid,gid);
		offset+=amount_read;
		amount_written+=amount_read;
		if (ret<0) {
			printf("IO error when writing to server, error %d\n", 
				ret);
			goto out;
		}
	}

/* FIXME time */

out:
	gettimeofday(&endtv,NULL);
	printdiff(&starttv, &endtv,&amount_written);

	close(fd);
	ml_close(vol,server_fullname,fp);
	return 0;

error:
	return -1;

}

static int retrieve_file(char * arg,int fd, int silent, 
	struct stat *stat, unsigned long long * amount_written)
{
	int flags=O_RDONLY;
	int ret=0;
	struct afp_file_info * fp;
	char path[PATH_MAX];
	off_t offset = 0;
#define BUF_SIZE 102400
	size_t size = BUF_SIZE;
	char buf[BUF_SIZE];
	int eof;
	unsigned long long total=0;
	struct timeval starttv,endtv;

	*amount_written=0;

	if (server==NULL) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	get_server_path(arg,path);

	gettimeofday(&starttv,NULL);

	if ((ret=ml_getattr(vol,path,stat))!=0) {
		printf("Could not get file attributes for file %s, return code %d\n",path,ret);
		goto error;
	}

	ret = ml_open(vol,path,flags,&fp);
	
	if (ret) {
		printf("Could not open %s on server, AFP error %d\n",arg,ret);
		goto error;
	}

	ret =1; /* to get the loop going */
	while (ret) 
	{
		memset(buf,0,BUF_SIZE);
		ret = ml_read(vol,path,buf,size,offset,fp,&eof);
		if (ret<=0) goto out;
		total+=write(fd,buf,ret);
		offset+=ret;
		if ((eof==1) || (ret==0)) goto out;
	}
out:

	if (fd>1) close(fd);
	ml_close(vol,path,fp);

	free(fp);

	if (silent==0) {
		gettimeofday(&endtv,NULL);
		printdiff(&starttv, &endtv,&total);
	}

	*amount_written=total;
	return 0;
error:
	return -1;
}

static int com_get_file(char * arg, int silent, 
	unsigned long long * total) 
{
	int fd;
	struct stat stat;
	char * localfilename;
	char filename[AFP_MAX_PATH];
	char getattr_path[AFP_MAX_PATH];
	int ret;

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}
	if ((escape_paths(filename,NULL,arg))) {
		printf("expecting format: get <filename>\n");
		goto error;
	}
	localfilename=basename(filename);

	printf("    Getting file %s\n",filename);

	if ((access(localfilename,W_OK)) && (errno!=ENOENT)) {
		printf("Trying to access %s\n",localfilename);
		perror("Access local file for write");
		goto error;
	}

	get_server_path(filename,getattr_path);

	if ((ret=ml_getattr(vol,getattr_path,&stat))!=0) {
		printf("Could not get file attributes for file %s, return code %d\n",filename,ret);
		goto error;
	}

	fd=open(localfilename,O_CREAT | O_TRUNC| O_RDWR, stat.st_mode);
	if (fd<0) {
		perror("Opening local file");
		goto error;
	}
	chmod(localfilename,stat.st_mode);
	chown(localfilename,stat.st_uid,stat.st_gid);
	retrieve_file(filename,fd,silent,&stat, total);

	close(fd);
	return 0;
error:
	return -1;
}

int com_get (char *arg)
{
	unsigned long long amount_written;
	char newpath[255];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}
	if ((arg[0]=='-') && (arg[1]=='r') && (arg[2]==' ')) {
		arg+=3;
		while ((arg) && (isspace(arg[0]))) arg++;
		snprintf(newpath,255,"%s/%s",curdir,arg);
		return recursive_get(newpath);
	} else 
		return com_get_file(arg,0, &amount_written);
error:
	return -1;
}


int com_view (char * arg)
{
	unsigned long long amount_written;
	char filename[AFP_MAX_PATH];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if ((escape_paths(filename,NULL,arg))) {
		printf("expecting format: view <filename>\n");
		goto error;
	}
	printf("Viewing: %s\n",filename);
	retrieve_file(filename,fileno(stdout),1,NULL, &amount_written);
	return 0;
error:
	return -1;
}

int com_rename (char * arg)
{

	char from_path[AFP_MAX_PATH], to_path[AFP_MAX_PATH];
	char full_from_path[AFP_MAX_PATH], full_to_path[AFP_MAX_PATH];
	struct stat stbuf;
	int ret;

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (escape_paths(from_path,to_path,arg)) {
		printf("Syntax: mv <fromfile> <tofile>\n");
		goto error;
	}

	get_server_path(from_path,full_from_path);
	get_server_path(to_path,full_to_path);
	printf("Moving from %s to %s\n",full_from_path,full_to_path);

	/* Make sure from_file exists */
	if ((ret=ml_getattr(vol,full_from_path,&stbuf))) {
		printf("Could not find file %s, error was %d\n",
			full_from_path,ret);
		goto error;
	}

	/* Make sure to_file doesn't exist */
	ret=ml_getattr(vol,full_to_path,&stbuf);
	if ((ret==0) && ((stbuf.st_mode & S_IFDIR)==0)) {
		printf("File %s already exists, error: %d\n", 
			full_to_path,ret);
		goto error;
	}

	if ((ret=ml_rename(vol,full_from_path, full_to_path))) goto error;

	return 0;
error:
	return -1;
}

int com_delete (char *arg)
{
	
	int ret;
	char server_fullname[AFP_MAX_PATH];
	char filename[AFP_MAX_PATH];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (escape_paths(filename,NULL,arg)) {
		printf("Syntax: del <filename>\n");
		goto error;
	}

	get_server_path(filename,server_fullname);

	if ((ret=ml_unlink(vol,server_fullname))) {
		printf("Could not remove %s, error code is %d\n",
			filename,ret);
		goto error;
	}
	printf("Removed file %s\n",filename);
	return (1);
error:
	return -1;
}

int com_mkdir(char *arg)
{
	
	int ret;
	char server_fullname[AFP_MAX_PATH];
	char filename[AFP_MAX_PATH];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (escape_paths(filename,NULL,arg)) {
		printf("Syntax: mkdir <dirname>\n");
		goto error;
	}

	get_server_path(filename,server_fullname);

	if ((ret=ml_mkdir(vol,server_fullname,0755))) {
		printf("Could not create directory %s, error code is %d\n",
			filename,ret);
		goto error;
	}
	printf("Created directory %s\n",filename);
	return 0;
error:
	return -1;
}

int com_rmdir(char *arg)
{
	
	int ret;
	char server_fullname[AFP_MAX_PATH];
	char filename[AFP_MAX_PATH];

	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected yet to a volume\n");
		goto error;
	}

	if (escape_paths(filename,NULL,arg)) {
		printf("Syntax: rmdir <dirname>\n");
		goto error;
	}

	get_server_path(filename,server_fullname);

	if ((ret=ml_rmdir(vol,server_fullname))) {
		printf("Could not remove directory %s, error code is %d\n",
			filename,ret);
		goto error;
	}
	printf("Removed directory %s\n",filename);
	return 0;
error:
	return -1;
}

int com_status(char * arg)
{
	int len=40960;
	char text[40960];

	afp_status_header(text,&len);
	printf("%s", text);

	len=40960;
	afp_status_server(server,text,&len);
	printf("%s", text);
	return 0;
}

int com_passwd(char * arg)
{
	char * p;
	int ret;
	char newpass[AFP_MAX_PASSWORD_LEN];

	if (!server) {
		printf("Not connected to a server\n");
		goto error;
	}
	p = getpass("New password: ");
	strncpy(newpass,p,AFP_MAX_PASSWORD_LEN);
	ret=ml_passwd(server,url.username,url.password,newpass);
	if (ret) {
		printf("Could not change password\n");
		goto error;
	}

	return 0;
error:
	return -1;
}

static void print_size(unsigned long l)
{

	if (l>(1073741824)) {
		printf("%4ldTb",l/1073741824);
		return;
	} 
	if (l>(1048576)) {
		printf("%4ldGb",l/1048576);
		return;
	} 
	if (l>(1024)) {
		printf("%4ldMb",l>>10);
		return;
	} 
	printf("%4ldKb\n",l);

}

int com_statvfs(char * arg)
{
	struct statvfs stat;
	unsigned long avail, used,total;
	unsigned int portion;
	int i;

	if ((server==NULL) || (vol==NULL)) {
		printf("Not connected to a volume\n");
		goto error;
	}
	ml_statfs(vol,"/",&stat);

	avail=stat.f_bavail*4;
	used=(stat.f_blocks-stat.f_bavail)*4;
	total=avail+used;

	portion = (unsigned int) (((float) used*100)/((float) avail+(float) used));

	printf("Volume ");
	for (i=strlen(vol->volume_name_printable)-6;i>0;i--) printf(" ");

	if (strstr(arg,"-h")) {
		printf("  Size   Used  Avail  Capacity\n");
		printf("%s ", vol->volume_name_printable);
		print_size(total); printf(" ");
		print_size(used); printf(" ");
		print_size(avail); printf(" ");
		printf("   %d%%\n",portion);
	} else {
		avail*=2;
		used*=2;
		total*=2;
		printf(" 512-blocks     Used     Available  Use%%\n");
		printf("%s %10ld %10ld %10ld  %d%%\n", vol->volume_name_printable,
			total, used, avail,portion);
	}
	return 0;
error:
	return -1;
}


int com_lcd(char * path)
{

	int ret;
	char curpath[PATH_MAX];

	ret=chdir(path);
	if (ret!=0) 
		perror("Changing directories");
	else {
		getcwd(curpath,PATH_MAX);
		printf("Now in local directory %s\n",curpath);
	}
	return ret;

}

/* Change to the directory ARG. */
int com_cd (char *arg)
{

	int ret;
	char newdir[AFP_MAX_PATH];
	char * p;
	struct stat stbuf;
	char tmppath[AFP_MAX_PATH];
	char * path = tmppath;

	memset(newdir,'\0',AFP_MAX_PATH);
	memset(path,'\0',AFP_MAX_PATH);

	if (server==NULL) {
		printf("You're not connected to a server yet\n");
		goto error;
	}

	if (strlen(url.volumename)==0) {
		char * volumename, *t;

		if (escape_paths(path,NULL,arg)) {
			printf("Syntax: cd <volumename>\n");
			goto error;
		}
		volumename=path;
		if ((t=strchr(path,'/'))) {
			path=t+1;
			*t='\0';
		} else 
			path=NULL;

		if (connect_volume(volumename)==0) {
			memcpy(url.volumename,vol->volume_name,
				AFP_VOLUME_NAME_UTF8_LEN);
			if (path==NULL) {
				return 0;
			}
		}
			else return -1;
	} else {
		if (escape_paths(path,NULL,arg)) 
			memset(path,'\0',AFP_MAX_PATH);
	}

	/* Chop off the last / */
	if (((strlen(path)>1) && (path[strlen(path)-1]=='/'))) 
		path[strlen(path)-1]='\0';
	if (((strlen(curdir)>1) && (curdir[strlen(curdir)-1]=='/')))
		curdir[strlen(curdir)-1]='\0';

	if (strncmp(path,"..", AFP_MAX_PATH)==0) {
		/* go back one */

		if (strlen(curdir)==1) {
			printf("Already at top level\n");
			return 0;
		}
		if ((p=strrchr(curdir,'/'))) {
			if (p==curdir) snprintf(curdir,AFP_MAX_PATH,"/");
			else *p='\0';
		} else {
			printf("Internal error\n");	
			goto error;
		}
	} else {
		if (path[0]=='/') {
			memcpy(newdir,path, AFP_MAX_PATH);
		} else  {
			if (((strlen(path)==1) && (path[0]=='/')) ||
			   (((strlen(curdir)==1) && (curdir[0]=='/')))) {

				snprintf(newdir,AFP_MAX_PATH,"/%s",path);
			} else  {
					snprintf(newdir,AFP_MAX_PATH,
						"%s/%s",curdir,path);
			}
		}

		ret=ml_getattr(vol,newdir,&stbuf);

		if ((ret==0) && (stbuf.st_mode & S_IFDIR)) {
			memcpy(curdir,newdir,AFP_MAX_PATH);
			printf("Now in directory %s\n",curdir);
		} else {
			if ((stbuf.st_mode & S_IFDIR)==0) {
				printf("%s is not a directory, mode is 0%o\n",newdir,
					stbuf.st_mode);
			} else {
				printf("Error %d\n",ret);
				goto error;
			}
		}

	
	}
	
	/* To change directory, get a file list and grab the did. */
	return 0;
error:
	return -1;
}

/* Print out the current working directory locally. */
int com_lpwd (char * ignore)
{
	char dir[255];
	getcwd(dir,255);
	printf("Now in local directory %s\n",dir);
	return 0;
}

/* Print out the current working directory. */
int com_pwd (char * ignore)
{
	if ((server==NULL) || (vol==NULL)) {
		printf("You're not connected to a volume yet\n");
		goto error;
	}

	printf("Now in directory %s on volume %s.\n",curdir, vol->volume_name_printable);
	return 0;
error:
	return -1;
}

static int get_dir(char * server_base, char * path, 
	unsigned long long * total)
{
	struct afp_file_info * p, *filebase;
	char total_path[AFP_MAX_PATH];	
	unsigned long long amount_written, local_total=0;

	if (strcmp(server_base,"/")==0) 
		snprintf(total_path,AFP_MAX_PATH,"/%s",path);
	else
		snprintf(total_path,AFP_MAX_PATH,"%s/%s",server_base,path);

	printf("Getting directory %s\n",total_path);

	mkdir(path,0755);
	chdir(path);

	if (ml_readdir(vol,total_path,&filebase)) goto error;
	if (filebase==NULL) goto out;
	for (p=filebase;p;p=p->next) {
		if (p->isdir) {
			get_dir(total_path,p->name, &amount_written);
		} else {
			snprintf(curdir,AFP_MAX_PATH,"%s",total_path);
			com_get_file(p->name,1, &amount_written);
		}
		local_total+=amount_written;
	}

	afp_ml_filebase_free(&filebase);

out:

	*total=local_total;
	chdir("..");
	
	return 0;
error:
	chdir("..");
	return -1;

}


static int recursive_get(char * path)
{
	char * dirc = strdup(path);
	char * base = basename(path);
	char * dir = dirname(dirc);

	struct timeval starttv, endtv;
	unsigned long long amount_written;

	gettimeofday(&starttv,NULL);
	get_dir(dir,base, &amount_written);
	gettimeofday(&endtv,NULL);

	printdiff(&starttv,&endtv, &amount_written);

	return 0;

}

static struct libafpclient afpclient = {
	.unmount_volume = NULL,
	.log_for_client = stdout_log_for_client,
	.forced_ending_hook = cmdline_forced_ending_hook,
	.scan_extra_fds = NULL,
	.loop_started = cmdline_loop_started,
};

static void * cmdline_server_startup(int recursive)
{

	struct stat stbuf;
	int ret;

	full_url=1;

	if (server_subconnect()) goto error;

	if (strlen(url.volumename)==0) {
		int i;
		char names[1024];
		afp_list_volnames(server,names,1024);
		printf("Specify a volume with 'cd volume'. Choose one of: %s\n",
			names);
		trigger_connected();
		return NULL;
	}

	ret=connect_volume(url.volumename);

	if (ret) {
		printf("Could not connect to volume %s on server\n",url.volumename);
		just_end_it_now(NULL);
		goto error;
	}


	trigger_connected();

	if (strlen(url.path)==0) 
		return NULL;

	ret=ml_getattr(vol,url.path,&stbuf);

	if (ret) {
		printf("Could not open %s on server\n",url.path);
		just_end_it_now(NULL);
		goto error;
	}

	if (stbuf.st_mode & S_IFDIR) {
		snprintf(curdir,AFP_MAX_PATH,"%s",url.path);
		printf("In directory %s\n",url.path);
		if (recursive) {
			recursive_get(url.path);
		}
		
	} else {
		com_get(url.path);
		just_end_it_now(NULL);
	}

	return NULL;

error:
	printf("Error\n");
	return (void *) -1;


}

void cmdline_afp_exit(void)
{

	afp_unmount_volume(vol);

}

void cmdline_afp_setup_client(void) 
{
	libafpclient_register(&afpclient);

}


int cmdline_afp_setup(int recursive, char * url_string)
{
	struct passwd * passwd;

	snprintf(curdir, sizeof(curdir), DEFAULT_DIRECTORY);
	if (init_uams()<0) return -1;

	afp_default_url(&url);

	passwd = getpwuid(getuid());
	strncpy(url.username, passwd->pw_name,AFP_MAX_USERNAME_LEN);
	if ((url_string) && (strlen(url_string)>1)) {


		if (afp_parse_url(&url,url_string,1)) {
			printf("Could not parse url.\n");
		}
		cmdline_getpass();
		trigger_connected();
		cmdline_server_startup(recursive);
	}

	return 0;
}

