#!/usr/bin/perl # Usage: # auto-upload -h (host) [-p (port)] -u (username) -w (password) # (files)... # e.g. # auto-upload -h ftp.foo.com -u whoever -w whatever foo.txt bar.txt require 5; use IO::Socket; # System error handler. sub sys_err { die("$0: $!\n"); } # Set program defaults. sub set_defaults { undef($host); $port = 4242; undef($user); undef($pwd); @files = (); } # Parse the command line arguments. sub parse_args { my($arg, $last); for $arg (@ARGV) { if ($last) { if ($last eq '-h') { $host = $arg; } elsif ($last eq '-p') { $port = int($arg); } elsif ($last eq '-u') { $user = $arg; } elsif ($last eq '-w') { $pwd = $arg; } undef($last); } elsif ($arg =~ /^-/) { if ($arg =~ /^-[hpuw]$/) { $last = $arg; } else { die("$0: unknown option\n"); } } else { push(@files, $arg); } } } # Input a single line from a socket and echo it. sub get_line { my($sock) = @_; my($in); ($in = <$sock>) =~ s/[\r\n]//g; print("< $in\n"); return $in; } # Output a single line to a socket and echo it. sub put_line { my($sock, $out, $show) = @_; print $sock ("$out\r\n"); if ($show) { print("> $show\n"); } else { print("> $out\n"); } } # Main program. set_defaults; parse_args; $host || die("$0: no host specified\n"); $user || die("$0: no user specified\n"); $pwd || die("$0: no password specified\n"); @files || die("$0: nothing to do\n"); $ftp = new IO::Socket::INET(Proto => 'tcp', PeerAddr => $host, PeerPort => $port) || die("$0: cannot open control connection\n"); binmode($ftp); $ftp->autoflush(1); $state = 0; while ($in = get_line($ftp)) { next if ($in =~ /^...-/); if ($state == 0) { die if ($in !~ /^2/); put_line($ftp, "USER $user"); $state = 1; } elsif ($state == 1) { die if ($in !~ /^3/); put_line($ftp, "PASS $pwd", "PASS (hidden)"); $state = 2; } elsif ($state == 2) { die if ($in !~ /^2/); put_line($ftp, "TYPE I"); $state = 3; } elsif ($state == 3) { die if ($in !~ /^2/); if (scalar(@files) == 0) { put_line($ftp, "QUIT"); $state = 8; next; } if ($files[0] =~ m:./:) { $pos = index($files[0], '/', 1); put_line($ftp, "MKD " . substr($files[0], 0, $pos)); $state = 4; } else { put_line($ftp, "PASV"); $state = 5; } } elsif ($state == 4) { # ignore the return code $pos = index($files[0], '/', $pos + 1); if ($pos == -1) { put_line($ftp, "PASV"); $state = 5; } else { put_line($ftp, "MKD " . substr($files[0], 0, $pos)); } } elsif ($state == 5) { die if ($in !~ /^2/); ($h1, $h2, $h3, $h4, $p1, $p2) = $in =~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/; $ftp_temp = new IO::Socket::INET(Proto => 'tcp', PeerAddr => "$h1.$h2.$h3.$h4", PeerPort => $p1 * 256 + $p2) || die("$0: cannot open upload connection\n"); binmode($ftp_temp); $ftp_temp->autoflush(1); put_line($ftp, "STOR " . $files[0]); $state = 6; } elsif ($state == 6) { die if ($in !~ /^1/); open(FH, $files[0]) || die("$0: cannot open file\n"); binmode(FH); while (sysread(FH, $data, 16384)) { $len = length($data); syswrite($ftp_temp, $data, $len) == $len || die("$0: cannot transfer file\n"); } close(FH); close($ftp_temp); shift(@files); $state = 3; } else { die if ($in !~ /^2/); exit(0); } } die;