Logo Search packages:      
Sourcecode: ksh version File versions  Download package

ifs_ftp.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1990-2007 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
/*
 * File: ifs_ftp.c
 */

#include "ifs_agent.h"
#include <stdio.h>

struct {
    int           version;
} ftp_data;

struct fileinfo {
    char    *fname;
    char    *info;
    int           ftype;
    off_t   fsize;
};

/*
 * name: ftp_addentry
 *    Add a time entry of filename into timestamp
 */
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;
}

/*
 * name: ftp_doentry
 *    add a full-path file to timestamp
 */
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;
}

/*
 * name: ftp_log2hash
 *    read log timestamp into an new allocated hash table
 */
/*
HASHTABLE *
ftp_log2hash( logfile )
char  *logfile;
{
    HASHTABLE     *htab;
    FILE    *fent;
    char    buf[ STRLEN ], *ptr;

    htab = hashalloc( NULL, HASH_set, HASH_ALLOCATE, 0 );
    if( (fent = fopen( logfile, "r" )) != NULL ) {
      while( fgets( buf, sizeof(buf), fent ) != NULL ) {
          if( (ptr = strchr( buf, ' ' )) != NULL ) {
            *ptr++ = '\0';
            hashput( htab, buf, ptr );
          }
      }
      fclose( fent );
    }
    return htab;
}
*/

/*
 * name: ftp_parselist
 *    parse the directory entry and check the file type.
 *    PS: support UNIX and NT ftp directory styles.
 */
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] ) {
    /* -rw-rw-r--  1 CSIE     ftp          1509 Sep 14 08:31 README */
    /* lrwxrwxrwx  1 CSIE     ftp             8 Nov 14  1993 dir -> pub/dir */
    /* drwxrwxr-x 25 CSIE     ftp          1024 Jan 31 04:56 pub */
      case 'd':  fi->ftype = 1;     /* unix directory */
      case '-':               /* unix regular file */
            if( *(ptr - 14) == ' ' ) {
                ptr -= 15;
                while( *ptr != ' ' )  ptr--;
                fi->fsize = (int)strtol( ptr+1, (char**)0, 0 );
            }
            break;

      case 'l':  fi->ftype = 2;     /* unix link file */
            fi->info = ptr;
            if( (ptr = strrchr( buf, ' ' )) == NULL ||
                strcmp( ptr, " ->" ) != 0 )
                return 0;
            *ptr = '\0';
            fi->fname = strrchr( buf, ' ' ) + 1;
            break;

    /* 08-01-95  02:55PM                13280 ROCNTS1.DOC */
    /* 08-01-95  02:55PM       <DIR>          DIR */
      case '0':                     /* NT 3.0 file system */
      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;
}

/*
 * name: ftp_makedents
 *    make virtual name space from the directory list.
 *    PS: the mtime of invalid file is set to 4 years ago.
 */
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 ) {        /* directory file */
            mkdir( finfo.fname, 0755 );
          } else if( finfo.ftype == 2 ) { /* link file */
            symlink( finfo.info, finfo.fname );
          } else {                        /* regular */
            MakeImageFile( finfo.fname, finfo.fsize );
            fprintf( fent, "%s %d\n", finfo.fname, modtime );
          }
      }
    }
    fclose( fent );
    fclose( fdir );
    return 0;
}

/*
 * ========================= ftp cs =========================
 */

/*
 *name: FtpCommand
 */
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;
}


/*
 *name: FtpReply
 */
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;
    }

    /* parsing multi-line response */
    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;
          }
      }
    }

    /* trim "...\r\n" to "...\n" */
    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 );
}

/*
 *name: FtpTalk
 */
int
FtpTalk( nFile, cmd, arg )
NetFile     *nFile;
char  *cmd, *arg;
{
    FtpCommand( nFile, cmd, arg );
    return FtpReply( nFile, NULL, 0 );
}

/*
 *name: FtpDataConnect
 */
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 );
}

/*
 *name: FtpXfer
 */
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 ) {   /* xfer result */
      cserrno = E_DATAXFER;
    }
    if( IfsAbortFlag )  FtpDisconnect( srv );
    return (cserrno == E_NIL ? 0 : -1);
}

/*
 *name: FtpXferFile
 */
int
FtpXferFile( srv, tmpfile )
struct server_info *srv;
char  *tmpfile;
{
    char    *lpath = srv->lpath;
    char    *rpath = srv->rpath;

    if( *rpath == '/' && rpath[1] == '~' )      /* home directory mount */ 
      rpath++;
    if( FtpXfer( srv, "RETR", rpath, tmpfile ) == -1 ) {
      return -1;
    }
    MakePath( lpath );
    rename( tmpfile, lpath );
    ftp_doentry( lpath );
    return 0;
}

/*
 *name: FtpXferDir
 */
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] == '~' )      /* home directory mount */
      rpath++;
    FtpCommand( nFile, "CWD", rpath );
    if( FtpReply( nFile, buf, sizeof(buf) ) != 250 ||
      strncmp( buf, "250 Unable to locate", 20 ) == 0 ) {
      /* typo of SMART_CD patch (of jdli@csie.nctu.edu.tw) */
      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;
}

/*
 *name: FtpConnect
 *    create a connection to remote host,
 *    save socket handler in the 'mode' field of mount_item.
 */
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 ) {   /* initial message */
      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;
}

/*
 *name: FtpDisconnect
 *    close the socket handler which stored in mount_item->mode
 */
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;
}

/*
 *name: FtpGetFile
 *    get a file/directory from remote host
 */
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 ) ) { /* maybe connection timeout */
      FtpDisconnect( srv );
      if( FtpConnect( srv ) == -1 )  /* make a new connection */
          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;
}

/*
 * name: FtpPutFile
 *    put a file from local file system to remote host
 */
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] == '~' )      /* home directory mount */ 
      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 ) {   /* transfer result */
      cserrno = E_DATAXFER;
      return -1;
    }
    return 0;
}

/*
 * name: FtpNop
 *    unimplement command
 */
int
FtpNop()
{
    return 0;
}

/*
 * name: FtpInit
 *    Initial the data and functions in agent_item
 */
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;
}


Generated by  Doxygen 1.6.0   Back to index