#!/usr/bin/perl # # FTPEdit # Auth: Peter Edlund, peter.edlund@capgemini.se # # distributed under the GNU license, # take your time and read it at www.gnu.org # # # 0.1, 980529 # initial revision # # rev. 0.2, 980530 # added passive mode ftp # added new file editing # added of for configuration file # # rev. 0.3, 980602 # added no transmission if we didn't change anything # added editor configuration # added backup option # added (a lot) more checks to see if everything is going alright # # rev. 0.4, 980605 # added default domain, to allow shorter command lines # # # except this file, you will need to have Perl and the Net::FTP module # installed and a file in your home directory named '.ftpedit', # that should hold configuration values like these: # # If you want to have something else than the default editor (vi) # you can, FIRST in the file, add a line like this # # editor /your/preferred/editor # # then for each ftpsite you want to enable remote editing for add # entries like this: # # [a name] - the 'domain'-name for the ftpserver # can be anything # address ftp.john.doe.com - the ftp address # user johndoe - your username # password your_password - your password (optional) # if you define passwd, chmod 600 .ftpedit # home /your/remote/home - the home directory on the ftp (optional) # I _really_ recommend setting the home # dir if you are doing much editing with # files in your home - since you wont have # to type the full path - 'ftpedit # .profile' will be enough to edit your # profile for instance # savedir /save/dir/locally - if you want all edited files to be saved # locally as well (optional) # (a bonus with using savedir is that once # the file is saved locally you can use # 'new' as an argument to skip the down- # load part, you already have the latest # file!) # backup yes - if set to 'yes', a 'file~' will be saved # on the ftpsite # # ALSO, the first 'domain' you specify in the configuration file will be # set to 'default domain', meaning that you can access that domain without # specifying any domain on the commandline (ex. 'ftpedit my/file.c' will do # instead of 'ftpedit mydomain my/file.c') # # # modules and pragmas # use strict; use Net::FTP; # predeclaration of subroutines sub usage; sub config_read; sub basename; sub query_passwd; sub create_directories; sub exiter; # predeclaration of variables my $temp = "/tmp/ftpedit.$$"; my (@opts, $home, $file, $ftp, $first_mtime, $second_mtime); # # Initial variable checking # die "Couldn't find your home directory\n" unless $home = $ENV{'HOME'}; die "Couldn't find a configuration file\n" unless -f "$home/.ftpedit"; # read in the configuration file values my %config = config_read("$home/.ftpedit"); # # check the arguments and augment as needed # specifically for primary domain # if(scalar(@ARGV) >= 1) { if(scalar(@ARGV) == 2 && $ARGV[1] eq 'new') { @opts = ( $config{'primary'}, $ARGV[0], $ARGV[1] ); } elsif(scalar(@ARGV) == 1) { @opts = ( $config{'primary'}, $ARGV[0] ); } else { @opts = @ARGV; } } else { usage() } my %params = %{ $config{$opts[0]} }; # check the basic configurational parameters die "Domain $opts[0] not configured\n" unless %params; die "Domain $opts[0] has no address configured\n" unless $params{'address'}; die "Domain $opts[0] has no username configured\n" unless $params{'user'}; # query for a password if its not defined in config file $params{'password'} = query_passwd($params{'user'}, $opts[0]) unless $params{'password'}; # what to do if the file we want doesn't start with '/' if ($opts[1] !~ m!^/!) { if ($params{'home'}) { $opts[1] = $params{'home'} .'/'. $opts[1]; } else { $opts[1] = '/'. $opts[1]; } } # # first, lets get the file from the remote system # unless we're creating a new file that is # if ($params{'savedir'}) { $file = "$params{'savedir'}$opts[1]"; } else { $file = $temp; } # create eventual directories not already there in # sub save structure create_directories($file) if $params{'savedir'}; unless ($opts[2] eq 'new') { $ftp = Net::FTP->new($params{'address'}, Passive => 'true'); unless ($ftp->login($params{'user'}, $params{'password'})) { print "Couldn't login to $params{'address'}!\n"; exiter(1, $file, $temp); } unless ($ftp->get($opts[1], $file)) { print "Couldn't get $opts[1]. No read access?\n"; exiter(1, $file, $temp); } $ftp->quit; } # take time for later checking if we really did anything to the file if(-f $file) { $first_mtime = (stat($file))[9]; } else { $first_mtime = 1; } # # now, let's do the actual editing # my $editor = $config{'editor'}; $editor = 'vi' unless $editor; system("$editor $file"); # # and then we put it back into the system where it belongs # print "\n"; # extra linebreak for visuality unless (-f $file) { print "Nothing edited, nothing will be transmitted.\n"; } else { $second_mtime = (stat($file))[9]; unless ($second_mtime > $first_mtime) { print "No changes to file, nothing will be transmitted.\n"; } else { $ftp = Net::FTP->new($params{'address'}, Passive => 'true'); unless ($ftp->login($params{'user'}, $params{'password'})) { print "Couldnt login to $params{'address'}!\n"; exiter(1, $file, $temp); } # if we should backup, lets rename the old file to file~ # if rename isn't implemented this will just fail if ($params{'backup'} =~ /^y/i) { if ($ftp->rename($opts[1], sprintf("%s~", $opts[1]))) { printf "backed up %s%s\n", $params{'address'}, $opts[1]; } else { printf "FAILED to back up %s%s\n", $params{'address'}, $opts[1]; } } unless ($ftp->put($file, $opts[1])) { print "FAILED to write $file to remote location. No write access?\n"; exiter(1, $file, $temp); } $ftp->quit; print "Saved in $params{'address'}$opts[1]\n"; } } # exit successfully exiter(0, $file, $temp); ################################################################################ # SUBROUTINES # sub exiter { my ($value, $file, $temp) = @_; qx/rm -f $temp/ if $file eq $temp; exit $value; } sub usage { printf " Usage: %s domain: the domainname specified in the configuration file if you want to connect to first domain specified in the configuration file, you need not pass this argument file: the file to edit if doesn't start with '/', your home directory will be prepended (if it is specified in ~/.ftpedit new: if you add 'new' at the end, you will create a new file to be stored (ie, I wont try to get the file, only store it) ", basename($0); exit 0; } sub create_directories { my ($file) = shift; my $root = ""; my @arr = split(/\//, $file); foreach (@arr) { next if $_ eq $arr[$#arr]; unless (-d qq($root/$_)) { die "Couldn't create directiry $root/$_\n" unless mkdir(qq($root/$_), 0755); } $root .= "/$_"; } } sub query_passwd { my ($user, $domain) = @_; print "Please give a password for user $user on domain $domain:\n"; print "Password: "; my $answer = ; chop($answer); return $answer; } sub basename { my ($foo) = shift; $foo =~ s/.*\/(.*)/$1/; return $foo; } sub config_read { my ($file, $flag) = @_; my (%hash, $main, $key, $value, $primary_dom); return 0 unless -f $file; open (FH, $file) || return 0; while () { next if /^[\s\t]*#/ || /^\W*$/; # skip comments and blanks s/(^.*?)\W*#.*/$1/; # remove end-of-line comments unless ($flag) { $main = $1, next if /^\W*\[(.+)\]/; $primary_dom = $main unless $primary_dom; # set pri domain } next unless ($key, $value) = split (/[\s\t]+/, $_, 2); chomp ($value); if ($main) { $hash{$main}{$key} = $value; } else { $hash{$key} = $value; } } $hash{'primary'} = $primary_dom; return %hash; }