#
#  $Id: Backup.py,v 1.24 1999/12/11 12:35:11 rob Exp $
#
#  Copyright 1998-1999 Rob Tillotson <robt@debian.org>
#  All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee or royalty is
#  hereby granted, provided that the above copyright notice appear in
#  all copies and that both the copyright notice and this permission
#  notice appear in supporting documentation or portions thereof,
#  including modifications, that you you make.
#
#  THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
#  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
#  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
#  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
#  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE!
#
"""Backup.
"""

__version__ = '$Id: Backup.py,v 1.24 1999/12/11 12:35:11 rob Exp $'

__copyright__ = 'Copyright 1998-1999 Rob Tillotson <robt@debian.org>'

import Pyrite
import Pyrite.Conduit

import os, string, stat, sys, re

from Sulfur import Options
from Sulfur.Options import O_MULTIPLE, Boolean, String
from Pyrite import protect_name, _

backup_dir = None

# exclusions:
#  - exclude-regexps  - regular expressions to match names
#  - exclude          - names to exclude
#  - exclude-creators - exclude these creator ids
#  - exclude-types    - exclude these types
#

def match(rlist, s):
    for r in rlist:
	m = re.match(r, s)
	if m: return m
    return None

class Conduit(Pyrite.Conduit.Conduit):
    name = 'Backup'
    version = Pyrite.version
    author = Pyrite.author
    url = ''
    description = _("Backs up the PalmOS device.")
    options = [
	Boolean('ignore_backup_bit', 1, _("Ignore backup bit?")),
	Boolean('incremental', 1, _("Incremental backup?")),
	Boolean('delete_old',1, _("Delete old databases?")), 
	Boolean('archive',1, _("Archive deleted databases?")),
	Boolean('debug', 0, _("Debugging output?")),

	Boolean('skip-old-applications', 1,
	       _("Skip applications that have already been backed up")),
	String('exclude', [], _("Names of databases to exclude"), None, (O_MULTIPLE,)),
	String('exclude-regexps', [], _("Regexps of database names to exclude"), None,
	       (O_MULTIPLE,)),
#	String('exclude-creators', [], _("Creators of databases to exclude"), None,
#	       (O_MULTIPLE,)),
#	String('exclude-types', [], _("Types of databases to exclude"), None,
#	       (O_MULTIPLE,)),
	]
    
    def __init__(self):
	Pyrite.Conduit.Conduit.__init__(self)

	self.__backup_list = []
	
    def presync(self, app):
	store = app.user_directory('backup')
	
	self.__backup_list = store.list()
	return 1

    def __should_backup(self, i, store):
	# test whether a database should be backed up, based on its info
	# structure.
	if not self.ignore_backup_bit and not i['flagBackup']:
	    if self.debug: self.log(_("excluding '%s' because backup bit not set") % name)
	    return 0
	name = i['name']
	# exclusions
	if self.__ex_names and name in self.__ex_names:
	    if self.debug: self.log(_("excluding '%s' based on name match") % name)
	    return 0
	if self.__ex_regexps and match(self.__ex_regexps, name):
	    if self.debug: self.log(_("excluding '%s' based on regexp match") % name)
	    return 0

	# incremental backup
	if not self.incremental or name not in self.__backup_list:
	    return 1
	
	try:
	    fi = store.info(name)
	except:
	    self.log(_("wtf? %s") % name)
	    return 0
	
	if self.debug:
	    self.log('%s %s/%s : %s/%s %s' % (name, fi['modifyDate'],
					      fi['modnum'],
					      i['modifyDate'],
					      i['modnum'],
					      i['flagBackup']))

	# the "backupbuddy rule"
	if (fi['modnum'] == i['modnum'] or
	    fi['modifyDate'] == i['modifyDate']) and \
	    fi['createDate'] == i['createDate']:
	    if self.debug: self.log(_("skipping '%s' based on header dates") % name)
	    return 0

	# the "don't back up big apps even when Palm OS is acting stupid" rule
	if self.__skip_apps and i['flagResource'] and i['type'] == 'APPL' and \
	   name in self.__backup_list:
	    if self.debug: self.log(_("skipping already-backed-up application '%s'") % name)
	    return 0

	return 1
    
    def __call__(self, app):
	store = app.user_directory('backup')

	# ??? create directory if doesn't exist?

	#backup_list = store.list()
	palm_list = app.remote.listinfo()

	self.__ex_names = self.get_option('exclude')
	self.__ex_regexps = self.get_option('exclude-regexps')
	self.__skip_apps = self.get_option('skip-old-applications')
	
	for i in palm_list:
	    name = i['name']
	    if self.__should_backup(i, store):
		self.log(_("backing up '%s'") % name)
		try:
		    app.remote.copy(store, name)
		except:
		    self.log('%s %s' % (sys.exc_info()[0],
					sys.exc_info()[1]))
		    self.log(_("failed to back up '%s'") % name)
		    try: store.delete(name)
		    except: pass

	    if name in self.__backup_list: self.__backup_list.remove(name)

	if self.delete_old:
	    self.__delete_list = self.__backup_list
#	    for fn in backup_list:
#		self.log('removing "%s" from backup directory' % fn)
#		try: store.delete(fn)
#		except: pass

	self.log(_("done"))


    def postsync(self, app):
	# hmm, this seems dangerous, can we guarantee the user name doesn't
	# change?  I say yes, it can change between presync and sync, but not
	# between sync and postsync, we have to make that rule or conduits will
	# be living in a world of hurt.
	store = app.user_directory('backup')
	if self.archive: astore = app.user_directory('backup-archive')
	if self.delete_old:
	    for fn in self.__delete_list:
		if self.archive:
		    self.log(_("archiving '%s'") % fn)
		    try: store.copy(astore, fn)
		    except: self.log(_("error copying '%s' to archive") % fn)
		self.log(_("removing '%s' from backup directory") % fn)
		try: store.delete(fn)
		except: self.log(_("error removing '%s' from backup") % fn)
		
