/* * Copyright (c) 2003 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. */ #include "config.h" #include #include #include #include #ifdef __APPLE__ #include #include #endif /* __APPLE__ */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_ZLIB #include #endif /* HAVE_ZLIB */ #include #include "applefile.h" #include "connect.h" #include "cksum.h" #include "base64.h" #include "code.h" #include "largefile.h" #include "progress.h" #include "mkprefix.h" extern void (*logger)( char * ); extern struct timeval timeout; extern int linenum; extern int verbose; extern int showprogress; extern int dodots; extern int cksum; extern int errno; extern int create_prefix; extern SSL_CTX *ctx; /* * Download requests path from sn and writes it to disk. The path to * this new file is returned via temppath which must be 2 * MAXPATHLEN. * * Return Value: * -1 - error, do not call closesn * 0 - OKAY * 1 - error, call closesn */ int retr( SNET *sn, char *pathdesc, char *path, char *temppath, mode_t tempmode, off_t transize, char *trancksum ) { struct timeval tv; char *line; int fd; unsigned int md_len; int returnval = -1; off_t size = 0; char buf[ 8192 ]; ssize_t rr; extern EVP_MD *md; EVP_MD_CTX mdctx; unsigned char md_value[ EVP_MAX_MD_SIZE ]; char cksum_b64[ SZ_BASE64_E( EVP_MAX_MD_SIZE ) ]; if ( cksum ) { if ( strcmp( trancksum, "-" ) == 0 ) { fprintf( stderr, "line %d: No checksum\n", linenum); fprintf( stderr, "%s\n", pathdesc ); return( 1 ); } EVP_DigestInit( &mdctx, md ); } if ( verbose ) printf( ">>> RETR %s\n", pathdesc ); if ( snet_writef( sn, "RETR %s\n", pathdesc ) < 0 ) { fprintf( stderr, "retrieve %s failed: 1-%s\n", pathdesc, strerror( errno )); return( -1 ); } tv = timeout; if (( line = snet_getline_multi( sn, logger, &tv )) == NULL ) { fprintf( stderr, "retrieve %s failed: 2-%s\n", pathdesc, strerror( errno )); return( -1 ); } if ( *line != '2' ) { fprintf( stderr, "%s\n", line ); return( 1 ); } /* Get file size from server */ tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { fprintf( stderr, "retrieve %s failed: 3-%s\n", pathdesc, strerror( errno )); return( -1 ); } size = strtoofft( line, NULL, 10 ); if ( verbose ) printf( "<<< %" PRIofft "d\n", size ); if ( transize >= 0 && size != transize ) { fprintf( stderr, "line %d: size in transcript does not match size " "from server\n", linenum ); fprintf( stderr, "%s\n", pathdesc ); return( -1 ); } /*Create temp file name*/ if ( snprintf( temppath, MAXPATHLEN, "%s.radmind.%i", path, getpid()) >= MAXPATHLEN ) { fprintf( stderr, "%s.radmind.%i: too long", path, (int)getpid()); return( -1 ); } /* Open file */ if (( fd = open( temppath, O_WRONLY | O_CREAT, tempmode )) < 0 ) { if ( create_prefix && errno == ENOENT ) { errno = 0; if ( mkprefix( temppath ) != 0 ) { perror( temppath ); return( -1 ); } if (( fd = open( temppath, O_WRONLY | O_CREAT, tempmode )) < 0 ) { perror( temppath ); return( -1 ); } } else { perror( temppath ); return( -1 ); } } if ( verbose ) printf( "<<< " ); /* Get file from server */ while ( size > 0 ) { tv = timeout; if (( rr = snet_read( sn, buf, MIN( sizeof( buf ), size ), &tv )) <= 0 ) { fprintf( stderr, "retrieve %s failed: 4-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error2; } if ( write( fd, buf, (size_t)rr ) != rr ) { perror( temppath ); returnval = -1; goto error2; } if ( cksum ) { EVP_DigestUpdate( &mdctx, buf, (unsigned int)rr ); } if ( dodots ) { putc( '.', stdout ); fflush( stdout ); } size -= rr; if ( showprogress ) { progressupdate( rr, path ); } } if ( close( fd ) != 0 ) { perror( path ); returnval = -1; goto error1; } if ( verbose ) printf( "\n" ); tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { fprintf( stderr, "retrieve %s failed: 5-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error1; } if ( strcmp( line, "." ) != 0 ) { fprintf( stderr, "%s", line ); fprintf( stderr, "%s\n", pathdesc ); returnval = -1; goto error1; } if ( verbose ) printf( "<<< .\n" ); /* cksum file */ if ( cksum ) { EVP_DigestFinal( &mdctx, md_value, &md_len ); base64_e( md_value, md_len, cksum_b64 ); if ( strcmp( trancksum, cksum_b64 ) != 0 ) { fprintf( stderr, "line %d: checksum in transcript does not match " "checksum from server\n", linenum ); fprintf( stderr, "%s\n", pathdesc ); returnval = 1; goto error1; } } return( 0 ); error2: close( fd ); error1: unlink( temppath ); return( returnval ); } #ifdef __APPLE__ /* * Return Value: * -1 - error, do not call closesn * 0 - OKAY * 1 - error, call closesn */ int retr_applefile( SNET *sn, char *pathdesc, char *path, char *temppath, mode_t tempmode, off_t transize, char *trancksum ) { int dfd, rfd; unsigned int md_len; int returnval = -1; off_t size; size_t rsize; ssize_t rc; char finfo[ FINFOLEN ]; char buf[ 8192 ]; char rsrc_path[ MAXPATHLEN ]; char *line; struct as_header ah; extern struct as_header as_header; extern struct attrlist setalist; struct as_entry ae_ents[ 3 ]; struct timeval tv; extern EVP_MD *md; EVP_MD_CTX mdctx; unsigned char md_value[ EVP_MAX_MD_SIZE ]; char cksum_b64[ SZ_BASE64_E( EVP_MAX_MD_SIZE ) ]; if ( cksum ) { if ( strcmp( trancksum, "-" ) == 0 ) { fprintf( stderr, "line %d: No checksum\n", linenum); fprintf( stderr, "%s\n", pathdesc ); return( 1 ); } EVP_DigestInit( &mdctx, md ); } if ( verbose ) printf( ">>> RETR %s\n", pathdesc ); if ( snet_writef( sn, "RETR %s\n", pathdesc ) < 0 ) { fprintf( stderr, "retrieve applefile %s failed: 1-%s\n", pathdesc, strerror( errno )); return( -1 ); } tv = timeout; if (( line = snet_getline_multi( sn, logger, &tv )) == NULL ) { fprintf( stderr, "retrieve applefile %s failed: 2-%s\n", pathdesc, strerror( errno )); return( -1 ); } if ( *line != '2' ) { fprintf( stderr, "%s\n", line ); return( 1 ); } /* Get file size from server */ tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { fprintf( stderr, "retrieve applefile %s failed: 3-%s\n", pathdesc, strerror( errno )); return( -1 ); } size = strtoofft( line, NULL, 10 ); if ( verbose ) printf( "<<< %" PRIofft "d\n", size ); if ( transize >= 0 && size != transize ) { fprintf( stderr, "line %d: size in transcript does not match size" "from server\n", linenum ); fprintf( stderr, "%s\n", pathdesc ); return( -1 ); } if ( size < ( AS_HEADERLEN + ( 3 * sizeof( struct as_entry )) + FINFOLEN )) { fprintf( stderr, "retrieve applefile %s failed: AppleSingle-encoded file too " "short\n", path ); return( -1 ); } /* read header to determine if file is encoded in applesingle */ tv = timeout; if (( rc = snet_read( sn, ( char * )&ah, AS_HEADERLEN, &tv )) <= 0 ) { fprintf( stderr, "retrieve applefile %s failed: 4-%s\n", pathdesc, strerror( errno )); return( -1 ); } if (( rc != AS_HEADERLEN ) || ( memcmp( &as_header, &ah, AS_HEADERLEN ) != 0 )) { fprintf( stderr, "retrieve applefile %s failed: corrupt AppleSingle-encoded file\n", path ); return( -1 ); } if ( cksum ) { EVP_DigestUpdate( &mdctx, (char *)&ah, (unsigned int)rc ); } /* name temp file */ if ( snprintf( temppath, MAXPATHLEN, "%s.radmind.%i", path, getpid()) >= MAXPATHLEN ) { fprintf( stderr, "%s.radmind.%i: too long", path, ( int )getpid()); return( -1 ); } /* data fork must exist to write to rsrc fork */ /* Open here so messages from mkprefix don't verbose dots */ if (( dfd = open( temppath, O_CREAT | O_EXCL | O_WRONLY, tempmode )) < 0 ) { if ( create_prefix && errno == ENOENT ) { errno = 0; if ( mkprefix( temppath ) != 0 ) { perror( temppath ); return( -1 ); } if (( dfd = open( temppath, O_CREAT | O_EXCL | O_WRONLY, tempmode )) < 0 ) { perror( temppath ); return( -1 ); } } else { perror( temppath ); return( -1 ); } } if ( verbose ) printf( "<<< " ); if ( dodots ) { putc( '.', stdout ); fflush( stdout ); } size -= rc; if ( showprogress ) { progressupdate( rc, path ); } /* read header entries */ tv = timeout; if (( rc = snet_read( sn, ( char * )&ae_ents, ( 3 * sizeof( struct as_entry )), &tv )) <= 0 ) { fprintf( stderr, "retrieve applefile %s failed: 5-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error2; } if ( rc != ( 3 * sizeof( struct as_entry ))) { fprintf( stderr, "retrieve applefile %s failed: corrupt AppleSingle-encoded file\n", path ); returnval = -1; goto error2; } /* Should we check for valid ae_ents here? YES! */ if ( cksum ) { EVP_DigestUpdate( &mdctx, (char *)&ae_ents, (unsigned int)rc ); } if ( dodots ) { putc( '.', stdout ); fflush( stdout ); } size -= rc; if ( showprogress ) { progressupdate( rc, path ); } /* read finder info */ tv = timeout; if (( rc = snet_read( sn, finfo, FINFOLEN, &tv )) <= 0 ) { fprintf( stderr, "retrieve applefile %s failed: 6-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error2; } if ( rc != FINFOLEN ) { fprintf( stderr, "retrieve applefile %s failed: corrupt AppleSingle-encoded file\n", path ); returnval = -1; goto error2; } if ( cksum ) { EVP_DigestUpdate( &mdctx, finfo, (unsigned int)rc ); } if ( dodots ) { putc( '.', stdout ); fflush( stdout ); } size -= rc; if ( showprogress ) { progressupdate( rc, path ); } /* * endian handling: swap bytes to architecture * native from AppleSingle big-endian. * * This doesn't affect the checksum, since we * already summed the header entries above. */ as_entry_hostswap( &ae_ents[ AS_RFE ] ); if ( ae_ents[ AS_RFE ].ae_length > 0 ) { /* make rsrc fork name */ if ( snprintf( rsrc_path, MAXPATHLEN, "%s%s", temppath, _PATH_RSRCFORKSPEC ) >= MAXPATHLEN ) { fprintf( stderr, "%s%s: path too long\n", temppath, _PATH_RSRCFORKSPEC ); returnval = -1; goto error2; } /* No need to mkprefix as dfd is already present */ if (( rfd = open( rsrc_path, O_WRONLY, 0 )) < 0 ) { perror( rsrc_path ); returnval = -1; goto error2; } for ( rsize = ae_ents[ AS_RFE ].ae_length; rsize > 0; rsize -= rc ) { tv = timeout; if (( rc = snet_read( sn, buf, ( int )MIN( sizeof( buf ), rsize ), &tv )) <= 0 ) { fprintf( stderr, "retrieve applefile %s failed: 7-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error3; } if (( write( rfd, buf, ( unsigned int )rc )) != rc ) { perror( rsrc_path ); returnval = -1; goto error3; } if ( cksum ) { EVP_DigestUpdate( &mdctx, buf, (unsigned int)rc ); } if ( dodots ) { putc( '.', stdout ); fflush( stdout ); } if ( showprogress ) { progressupdate( rc, path ); } } size -= ae_ents[ AS_RFE ].ae_length; if ( close( rfd ) < 0 ) { perror( rsrc_path ); goto error2; } } /* write data fork to file */ for ( rc = 0; size > 0; size -= rc ) { tv = timeout; if (( rc = snet_read( sn, buf, MIN( sizeof( buf ), size ), &tv )) <= 0 ) { fprintf( stderr, "retrieve applefile %s failed: 8-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error2; } if ( write( dfd, buf, ( unsigned int )rc ) != rc ) { perror( temppath ); returnval = -1; goto error2; } if ( cksum ) { EVP_DigestUpdate( &mdctx, buf, (unsigned int)rc ); } if ( dodots ) { putc( '.', stdout ); fflush( stdout); } if ( showprogress ) { progressupdate( rc, path ); } } if ( close( dfd ) < 0 ) { perror( temppath ); returnval = -1; goto error1; } if ( verbose ) printf( "\n" ); /* set finder info for newly decoded applefile */ if ( setattrlist( temppath, &setalist, finfo, FINFOLEN, FSOPT_NOFOLLOW ) != 0 ) { fprintf( stderr, "retrieve applefile %s failed: Could not set attributes\n", pathdesc ); returnval = -1; goto error1; } tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { fprintf( stderr, "retrieve applefile %s failed: 9-%s\n", pathdesc, strerror( errno )); returnval = -1; goto error1; } if ( strcmp( line, "." ) != 0 ) { fprintf( stderr, "%s", line ); fprintf( stderr, "%s\n", pathdesc ); returnval = -1; goto error1; } if ( verbose ) printf( "<<< .\n" ); if ( cksum ) { EVP_DigestFinal( &mdctx, md_value, &md_len ); base64_e(( char*)&md_value, md_len, cksum_b64 ); if ( strcmp( trancksum, cksum_b64 ) != 0 ) { fprintf( stderr, "line %d: checksum in transcript does not match " "checksum from server\n", linenum ); fprintf( stderr, "%s\n", pathdesc ); returnval = 1; goto error1; } } return( 0 ); error3: close( rfd ); error2: close( dfd ); error1: unlink( temppath ); return( returnval ); } #else /* !__APPLE__ */ int retr_applefile( SNET *sn, char *pathdesc, char *path, char *temppath, mode_t tempmode, off_t transize, char *trancksum ) { errno = EINVAL; return( -1 ); } #endif /* __APPLE__ */