#include "ifs_agent.h"
#include <stdio.h>
struct {
int version;
} ftp_data;
struct fileinfo {
char *fname;
char *info;
int ftype;
off_t fsize;
};
int
ftp_addentry( fname, ftime )
char *fname;
time_t ftime;
{
FILE *flog, *fent;
char flogname[ STRLEN ];
char buf[ STRLEN ];
int len;
len = strlen( fname );
sfsprintf( flogname, sizeof(flogname), "._log.%d", ftime );
if( (flog = fopen( flogname, "w" )) == NULL ) {
return -1;
}
fprintf( flog, "%s %d\n", fname, ftime );
if( (fent = fopen( "._log", "r" )) != NULL ) {
while( fgets( buf, sizeof(buf), fent ) != NULL ) {
if( strncmp( buf, fname, len ) != 0 || buf[len] != ' ' ) {
fprintf( flog, "%s", buf );
}
}
fclose( fent );
}
fclose( flog );
rename( flogname, "._log" );
return 0;
}
int
ftp_doentry( filepath )
char *filepath;
{
struct stat st;
char *ptr;
if( (ptr = strrchr( filepath, '/' )) == NULL )
return -1;
*ptr = '\0';
chdir( filepath );
*ptr++ = '/';
if( stat( ptr, &st ) == -1 )
return -1;
ftp_addentry( ptr, st.st_mtime );
return 0;
}
int
ftp_parselist( fi, buf )
struct fileinfo *fi;
char *buf;
{
char *ptr;
strtok( buf, "\n\r" );
if( (ptr = strrchr( buf, ' ' )) == NULL )
return 0;
*ptr++ = '\0';
fi->fname = ptr;
fi->ftype = 0;
fi->fsize = 0;
switch( buf[0] ) {
case 'd': fi->ftype = 1;
case '-':
if( *(ptr - 14) == ' ' ) {
ptr -= 15;
while( *ptr != ' ' ) ptr--;
fi->fsize = (int)strtol( ptr+1, (char**)0, 0 );
}
break;
case 'l': fi->ftype = 2;
fi->info = ptr;
if( (ptr = strrchr( buf, ' ' )) == NULL ||
strcmp( ptr, " ->" ) != 0 )
return 0;
*ptr = '\0';
fi->fname = strrchr( buf, ' ' ) + 1;
break;
case '0':
case '1':
fi->ftype = (buf[25] == 'D' ? 1 : 0);
if( *(ptr - 2) != ' ' ) {
ptr -= 2;
while( *ptr != ' ' ) ptr--;
fi->fsize = (int)strtol( ptr+1, (char**)0, 0 );
}
break;
default: return 0;
}
return 1;
}
int
ftp_makedents( tmpfile )
char *tmpfile;
{
struct fileinfo finfo;
time_t modtime;
char buf[ STRLEN ];
FILE *fdir, *fent;
modtime = cs.time - 86400 * (365 * 4 + 1);
if( (fdir = fopen( tmpfile, "r" )) == NULL )
return -1;
fent = fopen( "._log", "w" );
while( fgets( buf, sizeof( buf ), fdir ) != NULL ) {
if( ftp_parselist( &finfo, buf ) ) {
if( finfo.ftype == 1 ) {
mkdir( finfo.fname, 0755 );
} else if( finfo.ftype == 2 ) {
symlink( finfo.info, finfo.fname );
} else {
MakeImageFile( finfo.fname, finfo.fsize );
fprintf( fent, "%s %d\n", finfo.fname, modtime );
}
}
}
fclose( fent );
fclose( fdir );
return 0;
}
int
FtpCommand( nFile, cmd, arg )
NetFile *nFile;
char *cmd, *arg;
{
char buf[ STRLEN ];
if( cmd == NULL )
return -1;
sfsprintf( buf, sizeof(buf), arg ? "%s %s\r\n" : "%s\r\n", cmd, arg );
NetWrite( nFile, buf, strlen(buf) );
#ifdef DEBUG
if( strcmp( cmd, "PASS" ) == 0 )
debug_logit( "PASS *\r\n" );
else
debug_logit( buf );
#endif
return 0;
}
int
FtpReply( nFile, buf, bsize )
NetFile *nFile;
char *buf;
int bsize;
{
char tmpbuf[ STRLEN ];
char token[4], *ptr;
if( buf == NULL ) {
buf = tmpbuf;
bsize = sizeof(tmpbuf);
}
if( NetGets( nFile, buf, bsize ) == NULL ) {
debug_logit( "FtpReply: NetGets return NULL\n" );
return -1;
}
if( buf[3] == '-' ) {
strncpy( token, buf, 3 );
token[3] = ' ';
while( strncmp( buf, token, 4 ) != 0 ) {
if( NetGets( nFile, buf, bsize ) == NULL ) {
debug_logit( "Multi-line parse error\n" );
return -1;
}
}
}
if( (ptr = strchr( buf, '\r' )) != NULL && ptr[1] == '\n' ) {
*ptr++ = '\n';
*ptr = '\0';
}
debug_logit( buf );
return( (buf[0]-'0') * 100 + (buf[1]-'0') * 10 );
}
int
FtpTalk( nFile, cmd, arg )
NetFile *nFile;
char *cmd, *arg;
{
FtpCommand( nFile, cmd, arg );
return FtpReply( nFile, NULL, 0 );
}
NetFile *
FtpDataConnect( srv, nFile )
struct server_info *srv;
NetFile *nFile;
{
char reply[ STRLEN ];
char host[ STRLEN ];
char *arg[6], *ptr;
int port;
FtpCommand( nFile, "PASV", NULL );
if( FtpReply( nFile, reply, sizeof(reply) ) != 220 ||
(ptr = strchr( reply, '(' )) == NULL ) {
cserrno = E_CONNECT;
return NULL;
}
if( SplitFields( arg, 6, ptr+1, ',' ) < 6 ) {
cserrno = E_CONNECT;
return NULL;
}
sfsprintf( host, sizeof(host), "%s.%s.%s.%s", arg[0], arg[1], arg[2], arg[3] );
port = (int)strtol( arg[4], (char**)0, 0 ) * 256 + (int)strtol( arg[5], (char**)0, 0 );
return NetConnect( srv, host, port );
}
int
FtpXfer( srv, cmd, rpath, tmpfile )
struct server_info *srv;
char *cmd;
char *rpath;
char *tmpfile;
{
NetFile *nFile, *nData;
char buf[ STRLEN ];
int fd, len;
nFile = srv->mitem->nFile;
if( (nData = FtpDataConnect( srv, nFile )) == NULL ) {
if( IfsAbortFlag ) FtpDisconnect( srv );
cserrno = E_CONNECT;
return -1;
}
if( FtpTalk( nFile, cmd, rpath ) >= 200 ) {
if( IfsAbortFlag ) FtpDisconnect( srv );
NetClose( nData );
cserrno = E_DATAXFER;
return -1;
}
cserrno = E_NIL;
if( (fd = open( tmpfile, O_WRONLY|O_CREAT|O_TRUNC, 0644 )) < 0 ) {
cserrno = E_DATAXFER;
} else {
while( (len = NetRead( nData, buf, sizeof(buf) )) > 0 )
write( fd, buf, len );
close( fd );
}
NetClose( nData );
if( FtpReply( nFile, NULL, 0 ) != 220 ) {
cserrno = E_DATAXFER;
}
if( IfsAbortFlag ) FtpDisconnect( srv );
return (cserrno == E_NIL ? 0 : -1);
}
int
FtpXferFile( srv, tmpfile )
struct server_info *srv;
char *tmpfile;
{
char *lpath = srv->lpath;
char *rpath = srv->rpath;
if( *rpath == '/' && rpath[1] == '~' )
rpath++;
if( FtpXfer( srv, "RETR", rpath, tmpfile ) == -1 ) {
return -1;
}
MakePath( lpath );
rename( tmpfile, lpath );
ftp_doentry( lpath );
return 0;
}
int
FtpXferDir( srv, tmpfile )
struct server_info *srv;
char *tmpfile;
{
NetFile *nFile = srv->mitem->nFile;
char buf[ STRLEN ];
char *lpath = srv->lpath;
char *rpath = srv->rpath;
if( *rpath == '\0' ) rpath = "/";
if( *rpath == '/' && rpath[1] == '~' )
rpath++;
FtpCommand( nFile, "CWD", rpath );
if( FtpReply( nFile, buf, sizeof(buf) ) != 250 ||
strncmp( buf, "250 Unable to locate", 20 ) == 0 ) {
logit( "<ftp>: cwd error\n" );
cserrno = E_DATAXFER;
return -1;
}
logit( "<ftp>: xfer directory\n" );
if( FtpXfer( srv, "LIST", NULL, tmpfile ) == -1 )
return -1;
if( chdir( lpath ) == -1 ) {
sfsprintf( buf, sizeof(buf), "%s/._dir", lpath );
MakePath( buf );
chdir( lpath );
}
rename( tmpfile, "._dir" );
ftp_makedents( "._dir" );
symlink( srv->mitem->timeout, "._cache_time" );
return 0;
}
int
FtpConnect( srv )
struct server_info *srv;
{
struct mount_item *mitem = srv->mitem;
NetFile *nFile = mitem->nFile;
char pass[ 80 ], *user;
int port;
if( nFile != NULL ) {
if( FtpTalk( nFile, "NOOP", NULL ) == 200 ) {
sfsprintf( csusrmsg, sizeof(csusrmsg), "use fd:%d", nFile->socket );
return 0;
}
}
logit( "<ftp>: connect\n" );
port = (mitem->port ? mitem->port : 21);
if( (nFile = NetConnect( srv, mitem->host, port )) == NULL ) {
logit( "<ftp>: connect failed!\n" );
return -1;
}
if( FtpReply( nFile, NULL, 0 ) != 220 ) {
cserrno = E_CONNECT;
NetClose( nFile );
return -1;
}
logit( "<ftp>: login\n" );
user = mitem->user ? mitem->user : "anonymous";
if( mitem->passlen > 0 ) {
SecurityDataAccess( mitem->pass, pass, mitem->passlen );
} else {
strcpy( pass, "ftpcs@" );
}
if( FtpTalk( nFile, "USER", user ) != 330 ||
FtpTalk( nFile, "PASS", pass ) != 230 ) {
cserrno = E_USERAUTH;
NetClose( nFile );
return -1;
}
if( FtpTalk( nFile, "TYPE", "I" ) != 200 ) {
cserrno = E_CONNECT;
NetClose( nFile );
return -1;
}
mitem->nFile = nFile;
sfsprintf( csusrmsg, sizeof(csusrmsg), "use fd: %d", nFile->socket );
return 0;
}
int
FtpDisconnect( srv )
struct server_info *srv;
{
struct mount_item *mitem = srv->mitem;
NetFile *nFile = mitem->nFile;
if( nFile != NULL ) {
logit( "<ftp>: disconnect\n" );
NetClose( nFile );
mitem->nFile = NULL;
sfsprintf( csusrmsg, sizeof(csusrmsg), "closefd= %d", nFile->socket );
}
return 0;
}
int
FtpGetFile( srv )
struct server_info *srv;
{
struct mount_item *mitem = srv->mitem;
char tmpfile[ STRLEN ];
char *lpath = srv->lpath;
char *rpath = srv->rpath;
int ans;
MakeTmpFile( lpath, tmpfile, sizeof(tmpfile) );
if( mitem->nFile == NULL )
if( FtpConnect( srv ) == -1 )
return -1;
if( NetDataReady( mitem->nFile ) ) {
FtpDisconnect( srv );
if( FtpConnect( srv ) == -1 )
return -1;
}
if( *rpath == '\0' || DashD( lpath ) ) {
ans = FtpXferDir( srv, tmpfile );
} else if( DashF( lpath ) ) {
ans = FtpXferFile( srv, tmpfile );
} else {
if( (ans = FtpXferDir( srv, tmpfile )) == -1 )
ans = FtpXferFile( srv, tmpfile );
}
unlink( tmpfile );
return ans;
}
int
FtpPutFile( srv )
struct server_info *srv;
{
struct mount_item *mitem = srv->mitem;
NetFile *nFile, *nData;
char buf[ STRLEN ];
char *rpath;
int fd, len;
cserrno = E_NIL;
if( FtpConnect( srv ) == -1 )
return -1;
nFile = mitem->nFile;
logit( "<ftp>: store file\n" );
if( (fd = open( srv->lpath, O_RDONLY, 0 )) < 0 ) {
cserrno = E_DATAXFER;
return -1;
}
if( (nData = FtpDataConnect( srv, nFile )) == NULL ) {
close( fd );
return -1;
}
rpath = srv->rpath;
if( *rpath == '/' && rpath[1] == '~' )
rpath++;
if( FtpTalk( nFile, "STOR", rpath ) >= 200 ) {
logit( "<ftp>: stor error\n" );
cserrno = E_DATAXFER;
NetClose( nData );
close( fd );
return -1;
}
while( (len = read( fd, buf, sizeof(buf) )) > 0 ) {
NetWrite( nData, buf, len );
}
NetClose( nData );
close( fd );
if( FtpReply( nFile, NULL, 0 ) != 220 ) {
cserrno = E_DATAXFER;
return -1;
}
return 0;
}
int
FtpNop()
{
return 0;
}
int
FtpInit( tbl )
struct agent_item *tbl;
{
tbl->localdata = (char *) &ftp_data;
tbl->connect = FtpConnect;
tbl->disconnect = FtpDisconnect;
tbl->listdents = FtpGetFile;
tbl->getfile = FtpGetFile;
tbl->putfile = FtpPutFile;
tbl->userdef = FtpNop;
return 0;
}