# 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 "[email protected]" 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: