# Part of the A-A-P recipe executive: copy and move files (remotely) # Copyright (C) 2002 Stichting NLnet Labs # Permission to copy and use this file is specified in the file COPYING. # If this file is missing you can find it here: http://www.a-a-p.org/COPYING # # These are functions to copy and move files. Not only on the local system, # also to remote systems, using http://, ftp://, etc. # import os import os.path import string from Commands import aap_eval from Dictlist import string2dictlist from Error import * from Process import recipe_error from Util import * from Work import getrpstack from Message import * def copy_move(line_nr, globals, raw_arg, copy): """Implementation of ":copy -x from to" and ":move -x from to". When "copy" is non-zero copying is done, otherwise moving. "raw_arg" is the whole argument. "line_nr" is used for error messages.""" # Evaluate $VAR things arg = aap_eval(line_nr, globals, raw_arg, Expand(0, Expand.quote_aap)) rpstack = getrpstack(globals, line_nr) # TODO: check if all flags are handled properly # Copy flags: # -f forcefully overwrite # -i interactive, ask the user before overwriting # -L don't keep symbolic links, create new files and directories # -p preserve last modification time and permissions as much as possible # -r -R recursive, enter directories # -v verbose # Move flags: # -f forcefully overwrite # -i interactive, ask the user before overwriting # -v verbose try: flags, i = get_flags(arg, 0, (copy and "filLprRv") or "fiv") except UserError, e: recipe_error(rpstack, e) # -l: change symlinks to real files if 'L' in flags: keepsymlinks = 0 else: keepsymlinks = 1 # Get the remaining arguments, should be at least two of them. arglist = string2dictlist(rpstack, arg[i:]) if len(arglist) < 2: recipe_error(rpstack, _(":%s command requires at least two arguments") % (copy and "copy") or "move") from Remote import url_split3 import glob # Expand and check the "from" arguments. fromlist = [] for a in arglist[:-1]: fname = a["name"] fscheme, fmach, fpath = url_split3(fname) if fscheme == '': # It's a local file, expand ~user and wildcards. fl = glob.glob(os.path.expanduser(fname)) if len(fl) == 0: recipe_error(rpstack, _('No match for "%s"') % fname) # For copy without -r sources can't be a directory. if copy and not ('r' in flags or 'R' in flags): for l in fl: if os.path.isdir(l): recipe_error(rpstack, _('Copying a directory requires -r flag: %s') % l) fromlist.extend(fl) else: # It's a URL, append without expanding. if not copy: recipe_error(rpstack, _('Cannot move from a URL yet: "%s"') % fname) fromlist.append(fname) # Expand and check the "to" argument. tname = arglist[-1]["name"] tscheme, tmach, tpath = url_split3(tname) if tscheme == '': # For a local destination file expand ~user and wildcards. l = glob.glob(os.path.expanduser(tname)) if len(l) > 1: recipe_error(rpstack, _('More than one match for "%s"') % tname) if len(l) == 1: tname = l[0] # If there is more than one source, target must be a directory. if tscheme == '' and len(fromlist) > 1 and not os.path.isdir(tpath): recipe_error(rpstack, _('Destination must be a directory: "%s"') % tpath) if tscheme == "ftp": # # Prepare for uploading through ftp. # import ftplib # For "user+passwd@ftp.com" split at the @ passwd = '' acct = '' i = string.find(tmach, '@') if i > 0: user = tmach[:i] machname = tmach[i+1:] i = string.find(user, '+') if i > 0: passwd = user[i+1:] user = user[:i] else: prompt = (_('Enter password for user %s at %s: ') % (user, machname)) try: import getpass passwd = getpass.getpass(prompt) except: # TODO: should display stars for typed chars passwd = raw_input(prompt) else: user = '' machname = tmach import aapnetrc # obtain the login name and password from the netrc file try: n = aapnetrc.netrc() res = n.authenticators(machname) if res is None: user = '' else: user, acct, passwd = res except aapnetrc.NetrcParseError, e: pass # Try to open the connection to the ftp server. try: ftp = ftplib.FTP(machname) if user != '': if passwd != '': if acct != '': ftp.login(user, passwd, acct) else: ftp.login(user, passwd) else: ftp.login(user) else: ftp.login() except ftplib.all_errors, e: recipe_error(rpstack, (_('Cannot open connection for "%s"') % tname) + str(e)) elif tscheme == "scp": # # TODO: Prepare for uploading through scp. # pass elif tscheme != '': recipe_error(rpstack, _('Can only upload to scp:// and ftp://')) # # Loop over all "from" files. # for fname in fromlist: fscheme, fmach, fpath = url_split3(fname) # If the destination is a directory, append the source file name to the # destination directory. if ((tscheme != '' and len(fromlist) > 1) or (tscheme == '' and os.path.isdir(tname))): dest = os.path.join(tname, os.path.basename(fname)) destpath = os.path.join(tpath, os.path.basename(fname)) else: dest = tname destpath = tpath # If destination is a local file and "-i" flag used, ask for # overwriting. Use a special string to allow translating the response # characters. if tscheme == '' and 'i' in flags: if os.path.exists(dest): reply = raw_input(_('"%s" exists, overwrite? (y/n) ') % dest) if (len(reply) == 0 or not reply[0] in _("yY up to four chars that mean yes")[:4]): if copy: msg_warning(_("file not copied")) else: msg_warning(_("file not moved")) continue if fscheme == '' and tscheme == '': # # local file copy or move # if not copy: try: os.rename(fname, dest) done = 1 except: done = 0 # renaming failed, try copying if copy or not done: try: import shutil if os.path.isdir(fname): # TODO: if -p not used, set timestamps shutil.copytree(fname, dest, keepsymlinks) elif not copy or 'p' in flags: shutil.copy2(fname, dest) else: shutil.copy(fname, dest) except IOError, e: recipe_error(rpstack, (_('Cannot copy "%s" to "%s"') % (fname, dest)) + str(e)) if not copy: os.remove(fname) if copy: msg_info(_('Copied "%s" to "%s"') % (fname, dest)) else: msg_info(_('Moved "%s" to "%s"') % (fname, dest)) else: if fscheme != '': # download to local file from Remote import url_download try: if tscheme != '': tmpfile, rtime = url_download(fname) else: tmpfile, rtime = url_download(fname, dest) except IOError, e: recipe_error(rpstack, (_('Cannot download "%s" to "%s"') % (fname, dest)) + str(e)) if tscheme != '': msg_info(_('Attempting upload to "%s"' % dest)) if fscheme != '': # use temporary file fromfile = tmpfile else: fromfile = fpath if tscheme == 'ftp': try: f = open(fromfile, "r") ftp.storbinary("STOR " + destpath, f, 8192) f.close() except ftplib.all_errors, e: recipe_error(rpstack, (_('Cannot upload "%s" to "%s"') % (fname, dest)) + str(e)) elif tscheme == 'scp': cmd = 'scp %s %s:%s' % (fromfile, tmach, destpath) try: logged_system(cmd) except IOError, e: recipe_error(rpstack, (_('Cannot upload "%s" to "%s"') % (fname, dest)) + str(e)) if fscheme != '': # delete temporary file os.remove(tmpfile) msg_info(_('Uploaded to "%s"' % dest)) else: if not copy: os.remove(fname) msg_info(_('Moved "%s" to "%s"' % (fname, dest))) else: msg_info(_('Uploaded "%s" to "%s"' % (fname, dest))) # End of loop over all "from" files. if tscheme == 'ftp': ftp.quit() # close connection to ftp server if fscheme != '': from Remote import url_cleanup url_cleanup(fscheme) # vim: set sw=4 sts=4 tw=79 fo+=l: .