home *** CD-ROM | disk | FTP | other *** search
- /*
- * RCP for MPW
- * John Peterson, Taligent
- *
- * Based on Steve Falkenburg's Finger program,
- * along with an ancient version of rcp I wrote for Aegis ages ago.
- *
- * Usage:
- * rcp [mac_vol:mac_dir:]mac_file unixhost@[unixdir]:unixdir[:unixfile] [-p][-d][-n][-u username]
- * or
- * rcp unixhost@[unixdir:unixdir:]unixfile mac_vol:mac_dir[:mac_file] [-y][-p][-d][-n][-u username]
- *
- * The options are:
- * -d Print debugging information
- * -n Suppress newline conversion
- * (this is implied if copying a non 'TEXT' file from a Mac)
- * -p Print a progress message
- * -u Specify the Unix username to use
- * (this overrides the "UnixName" shell variable)
- * -y Overwrite an existing Mac file
- * (only valid if copying from a remote host to the Mac)
- *
- * It tries to be somewhat clever about converting the file notation,
- * so "kloo@:sub:xxx" copies files to a directory named "sub" in your
- * Unix home directory, but "kloo@tmp:xxx" specifies the file should be
- * copied to "/tmp" on the remote machine.
- *
- * You can set the MPW variable "UnixName" to be the default username
- * to use on the unix side.
- * As with Unix RCP, you need to have an entry in the .rhosts file on
- * the remote machine for rcp to work.
- *
- * To compile, put rcp.c in the same directory with the "Finger" source,
- * and use the lines below. The additional routines are described in
- * Develop #6 (Spring '91).
- *
- C rcp.c -o rcp.c.o
- link CvtAddr.c.o dnr.c.o TCPRoutines.c.o TCPHi.c.o rcp.c.o -o rcp -t 'MPST' -c 'MPS ' {CLibraries}StdCLib.o {Libraries}Runtime.o {Libraries}Interface.o {Libraries}ToolLibs.o
- *
- */
-
- /*
- * BUGS
- *
- * "::blah" isn't translated to "../blah"
- * Can't do recursive (directory) copies
- */
-
- #include "compat.h"
-
- #ifdef PROTOS
- #include <Types.h>
- #include <Memory.h>
- #include <Packages.h>
- #include <Files.h>
- #include <CursorCtl.h>
- #include <Desk.h> /* For SystemTask() */
- #endif
-
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
-
- #include "CvtAddr.h"
- #include "MacTCPCommonTypes.h"
- #include "TCPPB.h"
- #include "TCPHi.h"
-
- /*
- * Constants
- */
- #define RCMD_PORT 514
- #define BUFSIZE 16384
- #define TIMEOUT 20
- #define NEWLINE 0x0a /* Unix style newline */
-
- /*
- * Globals
- */
-
- char * source_host = NULL;
- char * source_file = NULL;
- char * dest_host = NULL;
- char * dest_file = NULL;
- char * unix_name = NULL;
-
- char buffer[BUFSIZE];
-
- Boolean progress_flag = false;
- Boolean debug_flag = false;
- Boolean stomp_dest_flag = false;
- Boolean cvt_nl_flag = true;
- Boolean user_nl_flag = false;
-
- unsigned long tcp_stream = NULL;
-
- /* For the TCP interface routines */
- Boolean gCancel = false; /* this is set to true if the user cancels an operation */
-
- /*
- * Error reporting
- */
-
- static void bomb( char * msg, char * arg )
- {
- if (tcp_stream)
- {
- (void) ReleaseStream( tcp_stream );
- }
- fprintf( stderr, "# %s%s\n", msg, arg ? arg : "" );
- exit(-1L);
- }
-
- static void check( OSErr err, char * msg )
- {
- if (err)
- {
- fprintf( stderr, "# Error: %d while %s\n", err, msg );
- if (tcp_stream)
- {
- (void) ReleaseStream( tcp_stream );
- }
- exit( err );
- }
- }
-
- /* this routine would normally be a callback for giving time to background apps */
-
- Boolean GiveTime(short)
- {
- SpinCursor(1);
- return true;
- }
-
- /*
- * Argument munging
- */
-
- /* Allocate a duplicate copy of a string */
-
- static char * strdup( char * s )
- {
- char * scopy;
- scopy = (char *) malloc( strlen( s ) + 1L );
- if (! scopy)
- bomb( "Heap space exhausted", NULL );
- strcpy( scopy, s );
- return scopy;
- }
-
- /* Get the leaf (filename) component of a pathname */
-
- static char * getleaf( char * fname )
- {
- char * p;
-
- /* Look for leaf component of filename */
- p = &fname[strlen( fname ) - 1];
- while ((p > fname) && (*p != '/') && (*p != ':'))
- p--;
- if ((*p == '/') || (*p == ':')) p++;
-
- return p;
- }
-
- /* Convert Unix newlines to Mac and vice-versa */
-
- static void convert_nl( char * buf, int length )
- {
- register int i;
-
- if (cvt_nl_flag)
- for (i = 0; i < length; i++)
- if (buf[i] == '\n')
- buf[i] = NEWLINE;
- else
- {
- if (buf[i] == NEWLINE)
- buf[i] = '\n';
- }
- }
-
- /* Convert Mac ':' directory marks to unix '/' */
-
- static char * unixify( char * fname )
- {
- register char * p = fname;
- Boolean macstyle = false;
-
- while (*p)
- {
- if (*p == ':')
- {
- macstyle = true;
- *p = '/';
- }
- p++;
- }
-
- /* On the mac, a leading ':' means a RELATIVE directory.
- * On unix, a leading '/' means an ABSOLUTE directory.
- * Still a bug: "::" should turn into "../"
- */
- if (macstyle)
- {
- if (*fname == '/')
- {
- p = fname;
- p++;
- p = strdup( p );
- }
- else
- {
- p = (char *) malloc( strlen( fname ) + 2L );
- *p = '/';
- p++;
- strcpy( p, fname );
- p--;
- }
- free( fname );
- return p;
- }
- else
- return fname;
- }
-
- /* Find out if a Mac file is text (i.e., should convert newlines) */
-
- Boolean istextfile( char * name )
- {
- OSErr err;
- char tmp[255];
- FInfo finderStuff;
-
- strcpy( tmp, name );
- c2pstr( tmp );
- err = GetFInfo( tmp, 0, &finderStuff );
- check( err, "Getting finder info for local file" );
- return( finderStuff.fdType == 'TEXT' );
- }
-
- /* Set Macintosh file types */
-
- static void set_filetype( char * name, OSType type, OSType creator )
- {
-
- OSErr err;
- char tmp[255];
- FInfo finderStuff;
-
- strcpy( tmp, name );
- c2pstr( tmp );
- err = GetFInfo( tmp, 0, &finderStuff );
- check( err, "Getting finder info for local file" );
- finderStuff.fdType = type;
- finderStuff.fdCreator = creator;
- err = SetFInfo( tmp, 0, &finderStuff );
- check( err, "Setting file type" );
- }
-
- /* Parse arguments, setting flags, etc */
-
- static void parse_args( int argc, char ** argv )
- {
- int i;
-
- if (argc < 2)
- bomb( "Must specify source & destination files", NULL );
-
- for (i = 1; i < argc; i++)
- {
- if (argv[i][0] == '-') /* Option flag */
- switch( argv[i][1] )
- {
- case 'p':
- progress_flag = true;
- break;
-
- case 'd':
- debug_flag = true;
- break;
-
- case 'y':
- stomp_dest_flag = true;
- break;
-
- case 'n':
- user_nl_flag = true;
- cvt_nl_flag = false;
- break;
-
- case 'u':
- if (i + 1 == argc)
- bomb( "Must supply unix user name for -u", NULL );
- i++;
- unix_name = strdup( argv[i] );
- break;
-
- default:
- bomb( "Unknown flag: ", argv[i] );
- break;
- }
- else /* Filename */
- {
- char * p;
- char * filename;
- char * host;
-
- if (source_file && dest_file)
- bomb( "Extra argument: ", argv[i] );
-
- p = argv[i];
- while ((*p) && (*p != '@') && (*p != '!')) /* Hunt for hostname mark */
- p++;
- if (*p) /* Found host */
- {
- *p = '\0';
-
- host = strdup( argv[i] );
- p++;
- filename = strdup( p );
- filename = unixify( filename );
- }
- else
- {
- filename = strdup( argv[i] );
- host = NULL;
- }
-
- if (source_file)
- {
- dest_file = filename;
- dest_host = host;
- }
- else
- {
- source_file = filename;
- source_host = host;
- }
- } /* else */
- } /* for */
-
- if (source_host && dest_host)
- bomb( "Source or destination must be a local filename", NULL );
-
- if (! (source_host || dest_host))
- bomb( "Use the duplicate command to copy local files", NULL );
-
- if (! unix_name)
- unix_name = getenv( "UnixName" );
-
- if (! unix_name)
- bomb( "Must set {UnixName} variable or use -u 'user'", NULL );
-
- /* See if just the dir name was given, if so, must fill in filename */
- if (dest_file[strlen( dest_file ) - 1] == (dest_host ? '/' : ':'))
- {
- char * p;
- p = getleaf( source_file );
- sprintf( buffer, "%s%s", dest_file, p );
- free( dest_file );
- dest_file = strdup( buffer );
- }
- }
-
- /*
- * Networking stuff
- */
-
- void open_remote()
- {
- OSErr err;
- char full_hostname[255];
- unsigned long ip_address, local_address;
- /* long save_dirid; */
- short save_vRefNum;
- int open_attempts = 5;
- unsigned short local_port = 514; /* Must be > 512 to be a BSD "priviledged port" */
-
- /* Totally disgusting - the DNR leaves the working directory in the sytem folder! */
- err = GetVol( NULL, &save_vRefNum );
- check( err, "caching working directory" );
-
- err = InitNetwork();
- check( err, "Opening network" );
-
- strcpy( full_hostname, source_host ? source_host : dest_host );
-
- err = ConvertStringToAddr( full_hostname, &ip_address );
- check( err, "Looking up hostname" );
-
- if (debug_flag)
- fprintf( stderr, "# Remote host %s is %d.%d.%d.%d\n",
- full_hostname,
- (ip_address >> 24) & 0xFF, (ip_address >> 16) & 0xFF,
- (ip_address >> 8) & 0xFF, ip_address & 0xFF );
-
- err = CreateStream( &tcp_stream, BUFSIZE );
- check( err, "Creating TCP I/O stream" );
-
- /* Try this multiple times - sometimes the port needs to settle for
- * consequitive opens */
- do
- {
- err = LowTCPOpenConnection( tcp_stream, TIMEOUT, ip_address, RCMD_PORT,
- &local_address, &local_port);
- if (err == openFailed)
- {
- long then, delay_ticks = 10;
-
- then = TickCount();
- while (TickCount() < then + delay_ticks)
- SystemTask();
- }
- }
- while ((err == openFailed) && (open_attempts-- != 0));
- check( err, "Opening connection" );
-
- if (debug_flag)
- fprintf( stderr, "# local addr is: %d.%d.%d.%d, port %d\n",
- (local_address >> 24) & 0xFF, (local_address >> 16) & 0xFF,
- (local_address >> 8) & 0xFF, local_address & 0xFF,
- local_port );
- if (debug_flag)
- fprintf( stderr, "# tcp_stream = %08x\n", tcp_stream );
-
- err = SetVol( NULL, save_vRefNum );
- check( err, "restoring working directory" );
- }
-
- /*
- * Check the response the server gave to the last request. 0=OK, others
- * indicate problems, followed by a message
- */
- static void check_response()
- {
- char * p = buffer;
- unsigned short length = BUFSIZE;
- OSErr err;
-
- err = RecvData( tcp_stream, buffer, &length, false );
- check( err, "Remote doesn't acknowledge");
-
- switch (buffer[0]) {
- case 0:
- return; /* Worked fine. */
-
- default: /* Remote err (terminate string first) */
- while (((p-buffer) < BUFSIZE) && (*p != NEWLINE) && (*p)) p++;
- p--;
- *p = '\0';
- p = buffer;
- p++;
- bomb( "Remote error: ", p );
- }
- }
-
- static void go_ahead()
- {
- OSErr err;
-
- buffer[0] = '\0';
- err = SendData( tcp_stream, buffer, (unsigned short) 1, false );
- check( err, "Sending ack to remote host" );
- }
-
- /*
- * With the connection open, go through the rcmd user verification. The
- * protocol used is described in RSHD(8C) of the BSD manual.
- */
- static void init_rcmd()
- {
- char * p;
- unsigned short buflen;
- OSErr err;
- int i;
-
- /* Layout of rcmd message is:
- * 0NsssssssNnnnnnnnNcccccccN
- * where '0' is ascii '0' followed by null (unused 2nd stream port)
- * 'N' is the null character ('\0')
- * 'sssss...' is the identity to use at the server.
- * 'nnnnn...' is the identity to use at the client.
- * 'cccc....' is the command to be passed to the remote shell.
- */
-
- buffer[0] = '0'; /* 2nd stream port (none) */
- buffer[1] = '\0';
-
- p = &buffer[2];
- strcpy( p, unix_name );
- p = &p[strlen(p) + 1]; /* Move past remote user name */
- strcpy( p, unix_name );
- p = &p[strlen(p) + 1]; /* Move past local user name */
-
- if (dest_host)
- sprintf( p, "rcp -t %s", dest_file );
- else
- sprintf( p, "rcp -f %s", source_file );
-
- p = &p[strlen(p) + 1]; /* Move past rcp command */
- buflen = p - buffer;
-
- if (debug_flag)
- {
- fprintf( stderr, "# Sending RCMD string: '" );
- for (i = 0; i < buflen; i++)
- fprintf( stderr, "%c", buffer[i] == '\0' ? '_' : buffer[i] );
- fprintf( stderr, "'\n" );
- }
- err = SendData( tcp_stream, buffer, buflen, false );
- check( err, "Starting copy" );
- check_response();
- }
-
- /*
- * File transfer
- */
-
- /* Send file from Mac to Unix host */
-
- static void send_file()
- {
- FILE * f;
- char * p;
- long file_len, return_len;
- OSErr err;
- int i;
-
- if (! (f = fopen( source_file, "r" )))
- {
- perror( "# Can't open" );
- bomb( "Copy failed sending file", NULL );
- exit( -1 );
- }
-
- if (! user_nl_flag) /* Only reset nl conversion if unspecified by user */
- if (! istextfile( source_file ))
- cvt_nl_flag = false;
-
- /* In accordance with the RCP protocol, first
- * C0xxx sss rrr\n
- * is sent with "xxx" as the files protection, "sss" as its decimal size,
- * and 'rrr' as the root part of its name. There are other possible
- * control records; 'T' for time, and 'D' and 'E' for directories, but
- * these are not implemented.
- */
-
- p = getleaf( dest_file ); /* Source file? */
-
- fseek( f, (long) 0, 2 ); /* Get file length */
- file_len = ftell( f );
- rewind( f );
-
- sprintf( buffer, "C0644 %d %s\n", file_len, p );
- buffer[strlen(buffer)-1] = NEWLINE; /* Switch to unix newline */
-
- err = SendData( tcp_stream, buffer, (unsigned short)strlen(buffer), false );
- check( err, "Sending control string" );
-
- check_response();
-
- for (i = 0; i < file_len; i += return_len )
- {
- return_len = fread( buffer, 1, sizeof( buffer ), f );
- if (return_len)
- {
- convert_nl( buffer, return_len );
- err = SendData( tcp_stream, buffer, (unsigned short) return_len, false );
- check( err, "Sending data" );
- }
- }
- fclose( f );
-
- go_ahead();
- }
-
- /* Get file from Unix host to the mac */
-
- static void recv_file()
- {
- FILE * f;
- unsigned short length;
- long i, remote_length;
- int file_mode;
- char remote_name[255];
- OSErr err;
-
- if (! stomp_dest_flag)
- {
- if (f = fopen( dest_file, "r" ))
- {
- fclose( f );
- bomb( "File exists (use -y to overwrite): ", dest_file );
- }
- }
-
- if (!(f = fopen( dest_file, "w" )))
- {
- perror( "Can't open for write" );
- bomb( "Copy failed", NULL );
- exit( -1 );
- }
-
- go_ahead();
-
- length = BUFSIZE;
-
- err = RecvData( tcp_stream, buffer, &length, false );
- check( err, "Getting control record" );
-
- /*
- * The control record tells us the protection, the (vax) filename, and
- * size (in that order, see send_file()). All we''re interested in is
- * the size. Also, the only type we can handle is 'C' (not 'D' or 'T').
- */
-
- if (buffer[0] < ' ') /* Ctl char means error */
- bomb( "Remote error:", buffer+1 );
-
- if (buffer[0] != 'C')
- bomb( "Unknown or unsupported control record received", NULL );
-
- if ( sscanf( buffer, "C %04o %d %s\n", &file_mode, &remote_length,
- &remote_name ) != 3 )
- bomb( "Bad control record received", NULL );
-
- go_ahead();
-
- for ( i = 0; i < remote_length; i += length ) {
-
- length = BUFSIZE;
- err = RecvData( tcp_stream, buffer, &length, false );
- convert_nl( buffer, (int) length );
- check( err, "Getting data" );
-
- /* Avoid swallowing the server's go-ahead */
- if ((i + length) > remote_length)
- length--;
-
- fwrite( buffer, 1, (int) length, f );
- }
-
- fclose( f );
-
- if (! user_nl_flag)
- set_filetype( dest_file, 'TEXT', 'MPS ' );
- else
- set_filetype( dest_file, '????', 'MPS ' );
-
- /** check_response (); This hangs because "go-ahead" is eaten above...
- I trust it for now.
- **/
- go_ahead();
- }
-
- void
- main( int argc, char ** argv )
- {
- OSErr err;
-
- parse_args( argc, argv );
- if (debug_flag)
- fprintf( stderr, "# source: [%s] %s\n# dest : [%s] %s\n# Remote User: %s\n",
- source_host ? source_host : "LOCAL", source_file ,
- dest_host ? dest_host : "LOCAL", dest_file, unix_name );
-
- open_remote();
- init_rcmd();
-
- if (dest_host)
- send_file();
- else
- recv_file();
-
- err = CloseConnection( tcp_stream );
- check( err, "Closing TCP connection" );
- ReleaseStream( tcp_stream );
-
- if (progress_flag)
- {
- if (source_host)
- fprintf( stderr, "# Copied %s@%s to %s\n", source_host, source_file, dest_file );
- else
- fprintf( stderr, "# Copied %s to %s@%s\n", source_file, dest_host, dest_file );
- }
- exit( 0 );
- }
-