Skip to content

Instantly share code, notes, and snippets.

@timsutton
Created February 21, 2013 16:17
Show Gist options
  • Save timsutton/5005855 to your computer and use it in GitHub Desktop.
Save timsutton/5005855 to your computer and use it in GitHub Desktop.
TaskExecutor.py helper script from Adobe's abominable Reader 10 update pkg
#!/usr/bin/python
import sys,os,plistlib,pwd,grp,stat,urllib,subprocess,traceback,re
taskPlist = unicode(sys.argv[2], sys.getfilesystemencoding())
appPath = unicode(sys.argv[3], sys.getfilesystemencoding())
patchStore = unicode(sys.argv[4], sys.getfilesystemencoding())
binaryPath = unicode(sys.argv[5], sys.getfilesystemencoding())
CLI_mode = sys.argv[6]
title = sys.argv[7]
rollbackStore = ''
if len(sys.argv) == 9:
rollbackStore = unicode(sys.argv[8], sys.getfilesystemencoding())
rollbackTask =[]
errorCodeDict = {
"PathAbsentForAttributesChange":108,
"ChangeOwnershipFailed":109,
"ChangePermissionFailed":110,
"OverwriteFileFailed":111,
"RemovePathFailed":112,
"FailedAttributesCheck":113,
"FailedChecksumMatch":114,
"RollbackError":115,
"GenericError":116,
}
def log(str):
try:
print repr(str)
except Exception, err:
print "Unable to log some text."
return
def runCmd(cmd, logOutput):
'''
execute a command
'''
status = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output,error) = status.communicate()
returncode = status.returncode
if returncode != 0 or error != '' or (logOutput == True and output != '') or enable_debug_logs == 1:
log((" cmd: %s") %(cmd))
log((" cmdRetCode [%d], cmdErrMsg [%s], cmdOutput [%s]") %(returncode, error.strip() , output.strip()))
return (output,error,returncode)
def installLocFromPkgId(pkgId):
'''
get installation location from package identifier
'''
installpath = ''
if pkgId == 'com.adobe.acrobat.10.viewer.app.pkg' or pkgId == 'com.adobe.acrobat.reader.10.reader.app.pkg':
installpath = os.path.dirname(appPath)
elif pkgId == 'com.adobe.acrobat.10.viewer.browser.pkg' or pkgId == 'com.adobe.acrobat.reader.10.reader.browser.pkg':
installpath = "/Library/Internet Plug-Ins"
elif pkgId == 'com.adobe.acrobat.10.viewer.appsupport.pkg' or pkgId == 'com.adobe.acrobat.reader.10.reader.appsupport.pkg':
installpath = "/Library/Application Support"
elif pkgId == 'com.adobe.acrobat.10.viewer.print_pdf_services.pkg':
installpath = "/Library/PDF Services"
elif pkgId == 'com.adobe.acrobat.10.viewer.print_automator.pkg':
installpath = "/Library/Automator"
elif pkgId == 'com.adobe.acrobat.10.viewer.preferences.pkg':
installpath = "/Library/Preferences"
else:
log(("Invalid package identifier obtained:%s.") %(pkgId))
raise Exception("GenericError")
return installpath
def replaceWithRenamedApplication(src,dest):
'''
if user has renamed the application
'''
destModified=''
if src.rfind("Adobe Acrobat Pro.app") != -1 and appPath.rfind("Adobe Acrobat Pro.app") == -1:
index = dest.rfind("Adobe Acrobat Pro.app")
destModified=dest[0:index] + dest[index:len(dest)].replace("Adobe Acrobat Pro.app",os.path.basename(appPath))
if src.rfind("Adobe Reader.app") != -1 and appPath.rfind("Adobe Reader.app") == -1:
index = dest.rfind("Adobe Reader.app")
destModified=dest[0:index] + dest[index:len(dest)].replace("Adobe Reader.app",os.path.basename(appPath))
if destModified != '':
return destModified
else:
return dest
def getSourceAndDestination(plistEntry):
pkgId = plistEntry["TargetPackageId"]
relativePath = plistEntry["RelativePath"]
langId = plistEntry["LangId"]
if langId != '' and langId is not None:
src = os.path.join(os.path.join(os.path.join(patchStore,langId),plistEntry["StagingRootDir"]),relativePath)
else: #if no langId then use the en_US directory from patchStore
src = os.path.join(os.path.join(os.path.join(patchStore,"en_US"),plistEntry["StagingRootDir"]),relativePath)
dest = os.path.join(installLocFromPkgId(pkgId),relativePath)
dest = replaceWithRenamedApplication(src,dest)
return (src,dest)
def verifyConditionForChangeAttributes(dest,isDir):
'''
file should exist for changing attributes
'''
if isDir == 0:
if os.path.islink(dest):
return
elif os.path.isfile(dest):
return
else:
if os.path.isdir(dest):
return
log(("ERROR: The path %s is invalid, for attributes change operation.") %(dest))
raise Exception("PathAbsentForAttributesChange",dest)
def verifyConditionForRemove():
return
def verifyConditionForOverwrite(dest,isDir):
'''
for overwriting a directory file with same name should not exist and vice versa
'''
if isDir == 0:
if os.path.isdir(dest):
log(("ERROR: The path %s is a directory which is supposed to be a file.") %(dest))
raise Exception("GenericError")
else:
if os.path.isfile(dest) or os.path.islink(dest):
log(("ERROR: The path %s is a file which is supposed to be a directory.") %(dest))
raise Exception("GenericError")
def verifyConditionForAdd(dest,isDir):
verifyConditionForOverwrite(dest,isDir)
def validatePreCondition(plistEntry):
'''
check for necessary condition before executing the tasks
'''
taskType = plistEntry["TaskType"]
isDir = plistEntry["IsDirectory"]
(src,dest) = getSourceAndDestination(plistEntry)
if taskType == 'ChangeAttributes':
verifyConditionForChangeAttributes(dest,isDir)
elif taskType == 'Remove':
verifyConditionForRemove()
elif taskType == 'Overwrite':
verifyConditionForOverwrite(dest,isDir)
elif taskType == 'Add':
verifyConditionForAdd(dest,isDir)
else:
log(("ERROR: %s:Invalid task type obtained.") %(taskType))
raise Exception ("GenericError")
def checkEncodedURLForBrowserPlugin(pdfViewer,plugin):
'''
upgrade only if pdfviewer.plist has the selected application path and plugin exists
'''
log("Info: Checking for browser plugin.")
if os.path.isdir(plugin) == False:
log(("Info: The plugin %s does not exist.") %(plugin))
return False
if os.path.isfile(pdfViewer+".plist"):
cmd = '/usr/bin/defaults read ' + '"' + pdfViewer + '"' + ' WebBrowserUsePath'
(output,error,returncode) = runCmd(cmd, False)
if returncode == 0 and output != '':
urlOfApppath = output.rstrip('\n')
urlOfApppath = urllib.unquote(urlOfApppath)
if urlOfApppath.find("file://localhost") == -1:
log(("ERROR: Cannot find the string file://localhost in encoded url."))
raise Exception("GenericError")
else:
urlOfApppath = urlOfApppath.split("file://localhost")[1]
else:
log(("ERROR: Unable to read WebBrowserUsePath from file: %s.") %(pdfViewer))
return False
urlOfApppath = unicode(urlOfApppath, sys.getfilesystemencoding())
log(("Info: The path obtained from %s is %s.") %(pdfViewer, urlOfApppath))
if urlOfApppath == appPath+"/":
return True
else:
return False
else:
log(("Info: The file %s does not exist.") %(pdfViewer+".plist"))
return False
def checkMajorVersion(path):
log(("Info: checking if major version is 10 in %s") %(path))
if os.path.isdir(path) == False:
log(("Info: Path %s not found.") %(path))
return False
infoPlist = os.path.join(os.path.join(path,"Contents"),"Info")
if os.path.isfile(infoPlist+".plist") == False:
log(("Info: The file %s does not exist.") %(infoPlist+".plist"))
return False
else:
cmd = '/usr/bin/defaults read ' + '"' + infoPlist + '"' + ' CFBundleVersion'
(output,error,returncode) = runCmd(cmd,True)
if returncode != 0 or output == '':
log(("ERROR: Encountered failure in reading %s.") %(infoPlist+".plist"))
raise Exception("GenericError")
if output.rstrip('\n').split(".")[0] == "10":
return True
else:
return False
def taskApplicable(plistEntry,shouldUpdateBrowserplugin,shouldUpdatePrintAutomator,shouldUpdatePrintPDFServices):
'''
perform task only if it is applicable
'''
pkgId = plistEntry["TargetPackageId"]
if pkgId == "com.adobe.acrobat.10.viewer.print_automator.pkg":
return shouldUpdatePrintAutomator
elif pkgId == "com.adobe.acrobat.10.viewer.print_pdf_services.pkg":
return shouldUpdatePrintPDFServices
elif pkgId == "com.adobe.acrobat.10.viewer.browser.pkg" or pkgId == "com.adobe.acrobat.reader.10.reader.browser.pkg":
return shouldUpdateBrowserplugin
else:
return True
def checkLanguage(plistEntry,langObtained):
'''
check if language is valid
'''
langId = plistEntry["LangId"]
if langId != '' and langId is not None:
if langObtained == langId:
return True
else:
return False
else:
return True
def assignAttributes(dest,plistEntry):
'''
path is invalid if there is no file, directory or link with that name
'''
if os.path.isfile(dest) == False and os.path.isdir(dest) == False and os.path.islink(dest) == False:
log(("ERROR: The path %s does not exist for changing the attributes.") %(dest))
raise Exception("PathAbsentForAttributesChange",dest)
owner = plistEntry["FileAttributes"]["NSFileOwnerAccountName"]
group = plistEntry["FileAttributes"]["NSFileGroupOwnerAccountName"]
perms = plistEntry["FileAttributes"]["NSFilePosixPermissions"]
perms = ("%o") %(perms)
cmd = '/usr/sbin/chown -h ' + '"' + owner + '"' + ':' + '"' + group + '"' + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The ChangeAttributes operation could not be completed for %s.") %(dest))
raise Exception("ChangeOwnershipFailed",dest)
cmd = '/bin/chmod -h ' + str(perms) + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The ChangeAttributes operation could not be completed for %s.") %(dest))
raise Exception("ChangePermissionFailed",dest)
def overwriteFile(src,dest,isDir):
if isDir == 0:
if os.path.islink(src):
cmd = '/bin/cp -fR ' + '"' + src + '"' + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The Overwrite operation could not be completed for %s.") %(dest))
raise Exception("OverwriteFileFailed",dest)
elif os.path.isfile(src):
cmd = '/usr/bin/ditto --rsrc ' + '"' + src + '"' + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
raise Exception("OverwriteFileFailed",dest)
else:
log(("ERROR: The source file %s does not exist.") %(src))
log(("ERROR: The Overwrite operation could not be completed for %s.") %(dest))
raise Exception("OverwriteFileFailed",dest)
else:
if os.path.isdir(dest) == False:
cmd = '/bin/mkdir -p ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The Overwrite operation could not be completed for %s.") %(dest))
raise Exception("OverwriteFileFailed",dest)
def removeFile(dest,isDir):
if isDir == 0:
if os.path.isfile(dest) or os.path.islink(dest):
cmd = '/bin/rm -f ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The Remove operation could not be completed for %s.") %(dest))
raise Exception("RemovePathFailed",dest)
else:
if os.path.isdir(dest):
if len(os.listdir(dest)) > 0:
log(("Info: The directory %s is not empty.") %(dest))
return
else:
cmd = '/bin/rmdir ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR: This command failed: %s.") %(cmd))
log(("ERROR: The Remove operation could not be completed for %s.") %(dest))
raise Exception("RemovePathFailed",dest)
def addFile(src,dest,isDir):
overwriteFile(src,dest,isDir)
def doOperation(plistEntry):
taskType = plistEntry["TaskType"]
isDir = plistEntry["IsDirectory"]
(src,dest) = getSourceAndDestination(plistEntry)
if taskType == 'Overwrite':
overwriteFile(src,dest,isDir)
assignAttributes(dest,plistEntry)
elif taskType == 'Remove':
err = removeFile(dest,isDir)
elif taskType == 'ChangeAttributes':
assignAttributes(dest,plistEntry)
elif taskType == 'Add':
addFile(src,dest,isDir)
assignAttributes(dest,plistEntry)
else:
log(("ERROR: Invalid task type obtained [%s].") %(taskType))
raise Exception("GenericError")
def getAttributesOfPath(dest):
stat_info = os.lstat(dest)
userObtained = stat_info.st_uid
groupObtained = stat_info.st_gid
permsObtained = stat.S_IMODE(stat_info.st_mode)
permsObtained = ("%o") %(permsObtained)
return (userObtained,groupObtained,permsObtained)
def checkAttributes(dest,plistEntry):
owner = plistEntry["FileAttributes"]["NSFileOwnerAccountName"]
group = plistEntry["FileAttributes"]["NSFileGroupOwnerAccountName"]
perms = plistEntry["FileAttributes"]["NSFilePosixPermissions"]
perms = ("%o") %(perms)
(ownerObtained,groupObtained,permsObtained) = getAttributesOfPath(dest)
ownerID = pwd.getpwnam(owner)[2]
groupID = grp.getgrnam(group)[2]
if ownerID != ownerObtained:
log(("ERROR: The attributes verification failed for user id for %s. Required userId = %d. Obtained userId = %d.") %(dest,ownerID,ownerObtained))
raise Exception("FailedAttributesCheck",dest)
if groupID != groupObtained:
log(("ERROR: The attributes verification failed for group id for %s. Required groupId = %d. Obtained groupId = %d.") %(dest,groupID,groupObtained))
raise Exception("FailedAttributesCheck",dest)
if perms != permsObtained:
log(("ERROR: The attributes verification failed for permissions for %s. Required permissions = %d. Obtained permissions = %d.") %(dest,perms,permsObtained))
raise Exception("FailedAttributesCheck",dest)
def verifyChecksum(dest,plistEntry):
cmd = '/usr/bin/cksum ' + ' "' + dest + '\"'
(output,error,returncode) = runCmd(cmd, False)
if returncode == 0 and output != '':
checksumObtained = output.rstrip('\n').split(' ')[0]
else:
log(("ERROR: This call failed: %s") %(cmd))
raise Exception("FailedChecksumMatch",dest)
checksum = plistEntry["ChecksumAfter"]
if checksum != int(checksumObtained):
log(("ERROR: The checksum verification failed. Required checksum = %d. Obtained checksum = %d.") %(checksum, int(checksumObtained)))
raise Exception("FailedChecksumMatch",dest)
def checkFileExists(src,dest,plistEntry,isDir):
if isDir == 0:
if os.path.islink(dest) == False and os.path.islink(src):
log(("ERROR: The link %s does not exist.") %(dest))
raise Exception("OverwriteFileFailed",dest)
elif os.path.isfile(dest) == False and os.path.isfile(src):
log(("ERROR: The file %s does not exist.") %(dest))
raise Exception("OverwriteFileFailed",dest)
checkAttributes(dest,plistEntry)
if os.path.isfile(dest) and os.path.islink(dest) == False: #validate checksum if the path is a genuine file and not a link
verifyChecksum(dest,plistEntry)
else:
if os.path.isdir(dest) == False:
log(("ERROR: The directory %s does not exist.") %(dest))
raise Exception("OverwriteFileFailed",dest)
def checkFileRemoved(dest,isDir):
if isDir == 0:
if os.path.islink(dest):
log(("ERROR: The link %s is not removed.") %(dest))
raise Exception("RemovePathFailed",dest)
if os.path.isfile(dest):
log(("ERROR: The file %s is not removed.") %(dest))
raise Exception("RemovePathFailed",dest)
def validatePostCondition(plistEntry):
taskType = plistEntry["TaskType"]
isDir = plistEntry["IsDirectory"]
(src,dest) = getSourceAndDestination(plistEntry)
if taskType == 'Overwrite':
checkFileExists(src,dest,plistEntry,isDir)
elif taskType == 'Remove':
checkFileRemoved(dest,isDir)
elif taskType == 'ChangeAttributes':
checkAttributes(dest,plistEntry)
elif taskType == 'Add':
checkFileExists(src,dest,plistEntry,isDir)
else:
log(("ERROR: Invalid task type obtained [%s].") %(taskType))
raise Exception("GenericError")
def printTaskDetails(plistEntry):
'''
executed in case of error to print details of task which failed
'''
log("Info: The details of the failed task are")
log((" TaskType = %s") %(plistEntry["TaskType"]))
log((" StagingRootDir = %s") %(plistEntry["StagingRootDir"]))
log((" RelativePath = %s") %(plistEntry["RelativePath"]))
log((" IsDirectory = %s") %(plistEntry["IsDirectory"]))
log((" TargetPackageId = %s") %(plistEntry["TargetPackageId"]))
log((" LangId = %s") %(plistEntry["LangId"]))
log((" ChecksumBefore = %s") %(plistEntry["ChecksumBefore"]))
log((" ChecksumAfter = %s") %(plistEntry["ChecksumAfter"]))
if plistEntry["TaskType"] != "Remove":
log((" NSFileOwnerAccountName = %s") %(plistEntry["FileAttributes"]["NSFileOwnerAccountName"]))
log((" NSFilePosixPermissions = %s") %(plistEntry["FileAttributes"]["NSFilePosixPermissions"]))
log((" NSFileGroupOwnerAccountName = %s") %(plistEntry["FileAttributes"]["NSFileGroupOwnerAccountName"]))
def checkAcroLocalePresent():
log("Info: Getting installed application locale")
acroLocalePlist = os.path.join(os.path.join(appPath,"Contents"),"AcroLocale")
if os.path.isfile(acroLocalePlist+".plist") == False:
log(("ERROR: The file %s does not exist.") %(acroLocalePlist+".plist"))
raise Exception("GenericError")
cmd = '/usr/bin/defaults read ' + '"' + acroLocalePlist + '"' + ' AcroInstallLocaleID'
(output,error,returncode) = runCmd(cmd, True)
if returncode == 0 and output != '':
acroLocalLang = output.rstrip('\n').split(' ')[0]
log(("Info: The installed app language obtained [%s].") %(acroLocalLang))
else:
log("ERROR: Unable to get the installed application locale")
raise Exception("GenericError")
return acroLocalLang
def prepareRollbackForChangeAttributes(dest,isDir,tasknum):
(ownerObtained,groupObtained,permsObtained) = getAttributesOfPath(dest)
rollbackDict = dict( #restore old attributes during rollback
task = "RollbackChangeAttributes",
path = dest,
owner = ownerObtained,
group = groupObtained,
permissions = permsObtained,
dir = isDir,
tasknumber = tasknum,
)
if enable_debug_logs == 1:
log((" Rollback dictionary: %d %s %s %s %s %s %d") %(rollbackDict["tasknumber"], rollbackDict["task"],rollbackDict["path"],rollbackDict["owner"],rollbackDict["group"],rollbackDict["permissions"],rollbackDict["dir"]))
rollbackTask.append(rollbackDict)
def prepareRollbackForRemove(dest,isDir,tasknum):
if os.path.lexists(dest):
(ownerObtained,groupObtained,permsObtained) = getAttributesOfPath(dest)
rollbackDict = dict( #if path existed before removal copy it in temporary location and put back during rollback
task = "RollbackOverwrite",
path = dest,
owner = ownerObtained,
group = groupObtained,
permissions = permsObtained,
dir = isDir,
tasknumber = tasknum,
)
if enable_debug_logs == 1:
log((" Rollback dictionary: %d %s %s %s %s %s %d") %(rollbackDict["tasknumber"], rollbackDict["task"],rollbackDict["path"],rollbackDict["owner"],rollbackDict["group"],rollbackDict["permissions"],rollbackDict["dir"]))
rollbackTask.append(rollbackDict)
rollbackPath = dest.lstrip('/')
if isDir == 0:
cmd = '/bin/mkdir -p ' + '"' + os.path.dirname(os.path.join(rollbackStore,rollbackPath)) + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR : The rollback preparation command %s failed.") %(cmd))
raise Exception("GenericError")
if os.path.islink(dest):
cmd = '/bin/cp -fR ' + '"' + dest + '"' + ' ' + '"' + os.path.join(rollbackStore,rollbackPath) + '"'
else:
cmd = '/usr/bin/ditto -rsrc ' + '"' + dest + '"' + ' ' + '"' + os.path.join(rollbackStore,rollbackPath) + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR : The rollback preparation command %s failed.") %(cmd))
raise Exception("GenericError")
#no need for backup in case of directory since we have the directory name and attributes
else:
return
def prepareRollbackForOverwrite(dest,isDir,tasknum):
if os.path.lexists(dest):
(ownerObtained,groupObtained,permsObtained) = getAttributesOfPath(dest)
rollbackDict = dict( #copy path in temporary location and put back during overwrite if path existed before overwriting
task = "RollbackOverwrite",
path = dest,
owner = ownerObtained,
group = groupObtained,
permissions = permsObtained,
dir = isDir,
tasknumber = tasknum,
)
if enable_debug_logs == 1:
log((" Rollback dictionary: %d %s %s %s %s %s %d") %(rollbackDict["tasknumber"], rollbackDict["task"],rollbackDict["path"],rollbackDict["owner"],rollbackDict["group"],rollbackDict["permissions"],rollbackDict["dir"]))
rollbackTask.append(rollbackDict)
rollbackPath = dest.lstrip('/')
if isDir == 0:
cmd = '/bin/mkdir -p ' + '"' + os.path.dirname(os.path.join(rollbackStore,rollbackPath)) + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR : The rollback preparation command %s failed.") %(cmd))
raise Exception("GenericError")
if os.path.islink(dest):
cmd = '/bin/cp -fR ' + '"' + dest + '"' + ' ' + '"' + os.path.join(rollbackStore,rollbackPath) + '"'
else:
cmd = '/usr/bin/ditto -rsrc ' + '"' + dest + '"' + ' ' + '"' + os.path.join(rollbackStore,rollbackPath) + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(("ERROR : The rollback preparation command %s failed.") %(cmd))
raise Exception("GenericError")
else:
rollbackDict = dict( #if path did not exist then remove it during rollback
task = "RollbackRemove",
path = dest,
owner = '',
group = '',
permissions = '',
dir = isDir,
tasknumber = tasknum,
)
if enable_debug_logs == 1:
log((" Rollback dictionary: %d %s %s %s %s %s %d") %(rollbackDict["tasknumber"], rollbackDict["task"],rollbackDict["path"],rollbackDict["owner"],rollbackDict["group"],rollbackDict["permissions"],rollbackDict["dir"]))
rollbackTask.append(rollbackDict)
def prepareRollbackForAdd(dest,isDir,tasknum):
prepareRollbackForOverwrite(dest,isDir,tasknum)
def prepareForRollback(plistEntry,tasknum):
taskType = plistEntry["TaskType"]
isDir = plistEntry["IsDirectory"]
(src,dest) = getSourceAndDestination(plistEntry)
if taskType == 'Overwrite':
prepareRollbackForOverwrite(dest,isDir,tasknum)
elif taskType == 'Remove':
prepareRollbackForRemove(dest,isDir,tasknum)
elif taskType == 'ChangeAttributes':
prepareRollbackForChangeAttributes(dest,isDir,tasknum)
elif taskType == 'Add':
prepareRollbackForAdd(dest,isDir,tasknum)
else:
log(("ERROR: Invalid task type obtained [%s].") %(taskType))
raise Exception ("GenericError")
def validatePlistFile(plist):
if plist is None:
log(("ERROR: The plist is obtained as None."))
raise Exception("GenericError")
i=0
for entry in plist:
if 'TaskType' not in entry:
log(("ERROR: The key TaskType is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['TaskType'] == '':
log(("ERROR: The value for key Tasktype is null in node %d.") %(i))
raise Exception("GenericError")
if 'StagingRootDir' not in entry:
log(("ERROR: The key StagingRootDir is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['StagingRootDir'] == '':
log(("ERROR: The value for key StagingRootDir is null in node %d.") %(i))
raise Exception("GenericError")
if 'RelativePath' not in entry:
log(("ERROR: The key RelativePath is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['RelativePath'] == '':
log(("ERROR: The value for key RelativePath is null in node %d.") %(i))
raise Exception("GenericError")
if 'IsDirectory' not in entry:
log(("ERROR: The key IsDirectory is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['IsDirectory'] != 0 and entry['IsDirectory'] != 1:
log(("ERROR: The value for key IsDirectory is neither 0 nor 1 in node %d.") %(i))
raise Exception("GenericError")
if 'TargetPackageId' not in entry:
log(("ERROR: The key TargetPackageId is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['TargetPackageId'] == '':
log(("ERROR: The value for key TargetPackageId is null in node %d.") %(i))
raise Exception("GenericError")
if 'LangId' not in entry:
log(("ERROR: The key LangId is missing in node %d.") %(i))
raise Exception("GenericError")
if 'ChecksumBefore' not in entry:
log(("ERROR: The key ChecksumBefore is missing in node %d.") %(i))
raise Exception("GenericError")
if 'ChecksumAfter' not in entry:
log(("ERROR: The key ChecksumAfter is missing in node %d.") %(i))
raise Exception("GenericError")
if entry['TaskType'] != 'Remove':
if 'FileAttributes' not in entry:
log(("ERROR: The key FileAttributes is missing in node %d.") %(i))
raise Exception("GenericError")
else:
if 'NSFileOwnerAccountName' not in entry['FileAttributes']:
log(("ERROR: The key NSFileOwnerAccountName is missing in FileAttributes in node %d.") %(i))
raise Exception("GenericError")
if entry['FileAttributes']['NSFileOwnerAccountName'] == '':
log(("ERROR: The value for key NSFileOwnerAccountName is null in node %d.") %(i))
raise Exception("GenericError")
if 'NSFilePosixPermissions' not in entry['FileAttributes']:
log(("ERROR: The key NSFilePosixPermissions is missing in FileAttributes in node %d.") %(i))
raise Exception("GenericError")
if 'NSFileGroupOwnerAccountName' not in entry['FileAttributes']:
log(("ERROR: The key NSFileGroupOwnerAccountName is missing in FileAttributes in node %d.") %(i))
raise Exception("GenericError")
if entry['FileAttributes']['NSFileGroupOwnerAccountName'] == '':
log(("ERROR: The key NSFileGroupOwnerAccountName is missing in FileAttributes in node %d.") %(i))
raise Exception("GenericError")
i = i + 1
log(("Info: Task plist file validation successful, file path: %s") %(taskPlist))
def doRollbackForRollbackTaskOverwrite(rollbackEntry):
'''
copy path stored in temporary area to original path
'''
dest = rollbackEntry["path"]
src = os.path.join(rollbackStore,dest.lstrip('/'))
isDir = rollbackEntry["dir"]
tasknum = rollbackEntry["tasknumber"]
shouldChangeAttributes = False
if isDir == 0:
if os.path.islink(src):
cmd = '/bin/cp -fR ' + '"' + src + '"' + ' ' + '"' + dest + '"'
shouldChangeAttributes = True
else:
cmd = '/usr/bin/ditto -rsrc ' + '"' + src + '"' + ' ' + '"' + dest + '"'
else:
cmd = '/bin/mkdir -p ' + '"' + dest + '"'
shouldChangeAttributes = True
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log(("ERROR: Rollback failed for task %d.") %(tasknum))
if err != '':
log(err)
return False
if shouldChangeAttributes == True:
return doRollbackForRollbackTaskChangeAttributes(rollbackEntry)
else:
return True
def doRollbackForRollbackTaskRemove(rollbackEntry):
'''
delete path during rollback
'''
dest = rollbackEntry["path"]
tasknum = rollbackEntry["tasknumber"]
if os.path.lexists(dest) == False:
return True
cmd = '/bin/rm -rf ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log(("ERROR: Rollback failed for task %d.") %(tasknum))
if err != '':
log(err)
return False
return True
def doRollbackForRollbackTaskChangeAttributes(rollbackEntry):
'''
restore old attributes during rollback
'''
dest = rollbackEntry["path"]
owner = rollbackEntry["owner"]
group = rollbackEntry["group"]
perms = rollbackEntry["permissions"]
tasknum = rollbackEntry["tasknumber"]
cmd = '/usr/sbin/chown -h ' + '"' + str(owner) + '"' + ':' + '"' + str(group) + '"' + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode:
log(cmd)
log(("ERROR: Rollback failed for task %d.") %(tasknum))
if err != '':
log(err)
return False
cmd = '/bin/chmod -h ' + str(perms) + ' ' + '"' + dest + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log(("ERROR: Rollback failed for task %d.") %(tasknum))
if err != '':
log(err)
return False
return True
def doRollback(rollbackEntry):
rolledBack = True
task = rollbackEntry["task"]
if task == 'RollbackOverwrite':
rolledBack = doRollbackForRollbackTaskOverwrite(rollbackEntry)
elif task == 'RollbackRemove':
rolledBack = doRollbackForRollbackTaskRemove(rollbackEntry)
elif task == 'RollbackChangeAttributes':
rolledBack = doRollbackForRollbackTaskChangeAttributes(rollbackEntry)
return rolledBack
def printInvalidTasksNumbers(invalidTasks):
if len(invalidTasks) > 0:
prev = invalidTasks[0]
curr = invalidTasks[0]
i = 1
while i < len(invalidTasks):
if invalidTasks[i] != curr + 1:
if prev != curr:
log(("Info: Tasks %d to %d are not applicable.") %(prev,curr))
else:
log(("Info: Task %d is not applicable.") %(prev))
prev = invalidTasks[i]
curr = invalidTasks[i]
else:
curr = invalidTasks[i]
i = i + 1
if prev != curr:
log(("Info: Tasks %d to %d are not applicable.") %(prev,curr))
else:
log(("Info: Task %d is not applicable.") %(curr))
def killAdobeResourceSynchronizer():
cmd = '/bin/ps -x -c -A | grep ' + 'AdobeResourceSynchronizer'
(output,error,returncode) = runCmd(cmd, True)
if output != '':
log("Info: AdobeResourceSynchronizer is running, trying to kill.")
appsToKill = output.split('\n')
for app in appsToKill:
parts = app.split()
if parts != []:
cmd = 'kill -s 15 ' + parts[0]
(output,error,returncode) = runCmd(cmd, True)
if returncode != 0:
log("Unable to kill AdobeResourceSynchronizer.")
if error != '':
log(error)
def printStacktrace(traceback):
funcnames = traceback.split('\n')
for func in funcnames:
log(func)
def doInitialProcessing(plist):
langObtained = checkAcroLocalePresent()
validatePlistFile(plist)
shouldUpdateBrowserplugin = False
shouldUpdatePrintAutomator = False
shouldUpdatePrintPDFServices = False
isBrowserPluginPrefOK = checkEncodedURLForBrowserPlugin("/Library/Preferences/com.adobe.acrobat.pdfviewer","/Library/Internet Plug-Ins/AdobePDFViewer.plugin")
isBrowserPluginVerOK = checkMajorVersion("/Library/Internet Plug-Ins/AdobePDFViewer.plugin")
shouldUpdateBrowserplugin = isBrowserPluginPrefOK and isBrowserPluginVerOK
if title == "TitleAcrobat":
shouldUpdatePrintAutomator = checkMajorVersion("/Library/Automator/Save as Adobe PDF.action")
shouldUpdatePrintPDFServices = checkMajorVersion("/Library/PDF Services/Save as Adobe PDF.app")
if shouldUpdateBrowserplugin == True:
log("Info: Browser Plugin should be updated.")
else:
log("Info: Skip updating Browser Plugin.")
if shouldUpdatePrintAutomator == True:
log("Info: Print Automator should be updated.")
else:
log("Info: Skip updating Print Automator.")
if shouldUpdatePrintPDFServices == True:
log("Info: Print PDF Service should be updated.")
else:
log("Info: Skip updating Print PDF Service.")
return (langObtained,shouldUpdateBrowserplugin,shouldUpdatePrintAutomator,shouldUpdatePrintPDFServices)
def restoreSignatureForPlugin(browserPlugin):
isBrowserPluginVerOK = checkMajorVersion(browserPlugin)
log("Info: Performing signature validation for Browser Plugin.")
if isBrowserPluginVerOK != True:
log("Info: Browser Plugin version not matched, skipping action.")
return True
cmd = '/usr/bin/codesign -v ' + '"' + browserPlugin + '"'
(output,error,returncode) = runCmd(cmd,True)
if returncode != 0 and re.search('invalid', error):
log(("Info: Signature seems broken for Browser plugin [%s].") %(browserPlugin))
log("Info: Try to restore the signature of the bundle.")
pluginResourcePath=browserPlugin+'/Contents/Resources'
for dirItem in os.listdir(pluginResourcePath):
resourceDirToProcess=pluginResourcePath + "/" + dirItem
log(("Info: Checking resource folder [%s].") %(resourceDirToProcess))
if os.path.isdir(resourceDirToProcess) and not os.path.islink(resourceDirToProcess) and re.search('\.lproj$',resourceDirToProcess):
log(("Info: Processing resource folder [%s].") %(resourceDirToProcess))
removeOldStyleStringFileInFolder(resourceDirToProcess, "InfoPlist.strings", "Localizable.strings")
else:
log("Info: Signature is Valid.")
return True
def removeOldStyleStringFileInFolder(resourceDirToProcess, fileToCheckAndDelete, fileToCheckOnly):
filePathToCheckAndDelete =resourceDirToProcess+"/"+fileToCheckAndDelete
filePathToCheckOnly =resourceDirToProcess+"/"+fileToCheckOnly
if os.path.lexists(filePathToCheckAndDelete) and os.path.isfile(filePathToCheckAndDelete) and not os.path.islink(filePathToCheckAndDelete):
if os.path.lexists(filePathToCheckOnly) and os.path.isfile(filePathToCheckOnly) and not os.path.islink(filePathToCheckOnly):
log("Info: Both old and new style string resources present.")
log(("Info: Removing old style string resources [%s].") %(filePathToCheckAndDelete))
if not os.remove(filePathToCheckAndDelete):
log("Info: Removed old style string resources.")
return True
else:
log("Info: Fail to removed old style string resources.")
return False
else:
log("Info: Only old style String resources present.")
else:
log("Info: Old style String resources not present.")
log("Info: No need to remove any string file.")
return False
def updatePDFSettingsPlist():
acroPDFSettingsPlistFile = 'com.adobe.PDFAdminSettings.plist'
acroPDFSettingsPlistFilePath = '/Library/Preferences/com.adobe.PDFAdminSettings.plist'
installerAcroSettingsPlistFile = 'PDFAdminSettings.plist'
tmpPlistFile = 'com.adobe.PDFAdminSettingsTmp.plist'
finalPlistFile = 'com.adobe.PDFAdminSettingsFinal.plist'
installerAcroSettingsPlistFilePath = os.path.join( os.path.dirname(taskPlist), installerAcroSettingsPlistFile)
if os.path.lexists(installerAcroSettingsPlistFilePath):
log("Info: Processing PDFAdminSettings.plist file.")
else:
log("Info: Processing PDFAdminSettings.plist file is done only for Acrobat.")
log("Info: Skip Processing PDFAdminSettings.plist file.")
return False
if os.path.lexists(acroPDFSettingsPlistFilePath):
(ownerObtained,groupObtained,permsObtained) = getAttributesOfPath(acroPDFSettingsPlistFilePath)
else:
log(("ERROR: File not found %s.") %(acroPDFSettingsPlistFilePath))
log(("ERROR: Skip updating File %s.") %(acroPDFSettingsPlistFilePath))
return False
src = acroPDFSettingsPlistFilePath
dest = os.path.join(rollbackStore,acroPDFSettingsPlistFile)
addFile(src,dest,0)
tmpPlistFilePath = os.path.join(rollbackStore,tmpPlistFile)
addFile(dest,tmpPlistFilePath,0)
# Do processing on tmpPlistFilePath
finalPlistFilePath = os.path.join(rollbackStore,finalPlistFile)
if os.path.lexists(installerAcroSettingsPlistFilePath) and os.path.lexists(tmpPlistFilePath) :
installerAcroSettingPlist = plistlib.readPlist(installerAcroSettingsPlistFilePath)
# Convert the file format to xml before processing.
cmd = '/usr/bin/plutil -convert xml1 ' + '"' + tmpPlistFilePath + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log("ERROR: plutil failed for PDFSettings plist file %s.") %(tmpPlistFilePath)
if err != '':
log(err)
return False
tmpPlist = plistlib.readPlist(tmpPlistFilePath)
isUpdated = False
if tmpPlist.get("PDFSettingsPath") is None:
tmpPlist["PDFSettingsPath"] = installerAcroSettingPlist["PDFSettingsPath"]
isUpdated = True
log("Info: Updating PDFSettingsPath.")
if tmpPlist.get("PDFSettings") is None:
tmpPlist["PDFSettings"] = installerAcroSettingPlist["PDFSettings"]
isUpdated = True
log("Info: Updating PDFSettings.")
if isUpdated == True:
plistlib.writePlist(tmpPlist, finalPlistFilePath)
else:
log(("ERROR: Unable to find installer AcroSettingsPlist File [%s] or [%s].") %(installAcroSettingsPlistFilePath, tmpPlistFilePath))
log(("ERROR: Skip updating File %s.") %(acroPDFSettingsPlistFilePath))
return False
# we get the final updated plist file in rollback store.
if isUpdated == True:
if os.path.lexists(finalPlistFilePath) :
log("Info: Intermediate file created successfully.")
overwriteFile(finalPlistFilePath, acroPDFSettingsPlistFilePath, 0)
else:
log(("ERROR: Unable to create updated AcroSettingsPlist File [%s].") %(finalPlistFilePath))
log(("ERROR: Skip updating File %s.") %(acroPDFSettingsPlistFilePath))
return False
log("Info: Updated PDFAdminSettings.plist file, Now setting the permissions.")
cmd = '/usr/sbin/chown -h ' + '"' + str(ownerObtained) + '"' + ':' + '"' + str(groupObtained) + '"' + ' ' + '"' + acroPDFSettingsPlistFilePath + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log("ERROR: chown failed for PDFSettings plist file %s.") %(acroPDFSettingsPlistFilePath)
if err != '':
log(err)
return False
cmd = '/bin/chmod -h ' + str(permsObtained) + ' ' + '"' + acroPDFSettingsPlistFilePath + '"'
(out,err,returncode) = runCmd(cmd, True)
if returncode != 0:
log(cmd)
log(("ERROR: chmod failed for PDFSettings plist file %s.") %(acroPDFSettingsPlistFilePath))
if err != '':
log(err)
return False
else:
log(("Info: File [%s] is already updated, skip updating file.") %(acroPDFSettingsPlistFilePath))
log("Info: Done Processing PDFAdminSettings.plist file.")
return True
def beginScan(isPreinstall):
retval = 0
plist = ''
sudoCmd = 'sudo '
try:
if os.environ['USER'] != '':
sudoCmd = sudoCmd + '-u ' + os.environ['USER']
except Exception, e:
sudoCmd = 'sudo '
try:
plist = plistlib.readPlist(taskPlist)
(langObtained,shouldUpdateBrowserplugin,shouldUpdatePrintAutomator,shouldUpdatePrintPDFServices) = doInitialProcessing(plist)
except Exception, err:
log("EXCEPTION: Caught an exception during initial processing.")
printStacktrace(traceback.format_exc())
retval = errorCodeDict["GenericError"]
if CLI_mode == "0":
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + 'GenericError'
try:
runCmd(cmdline, True)
except Exception, e:
log("ERROR: Fail to show UI alert.")
if e != '':
log(e)
return retval
if isPreinstall == True:
i = 0
try:
invalidTasks = []
log("Info: Pre-validating all the tasks.")
while i < len(plist):
if taskApplicable(plist[i], shouldUpdateBrowserplugin, shouldUpdatePrintAutomator,shouldUpdatePrintPDFServices) == True and checkLanguage(plist[i],langObtained) == True:
validatePreCondition(plist[i])
else:
invalidTasks.append(i)
i = i + 1
printInvalidTasksNumbers(invalidTasks)
log("Info: Pre-validation for all tasks successful.")
except Exception, err:
log(("EXCEPTION: Caught an exception while validating precondition for task %d.") %(i))
failedTask = i
printStacktrace(traceback.format_exc())
failureFilePath = ''
if not err.args[0] in errorCodeDict:
errorType = "GenericError"
else:
errorType = err.args[0]
if len(err.args) == 2:
failurefilepath = err.args[1]
log(("Info: Failure file path %s") %(failureFilePath))
retval = errorCodeDict[errorType]
if CLI_mode == "0":
if failureFilePath != '':
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + errorType + ' ' + '"' + failureFilePath + '"'
else:
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + errorType
try:
runCmd(cmdline, True)
except Exception, e:
log("ERROR: Fail to show UI alert.")
if e != '':
log(e)
printTaskDetails(plist[failedTask])
return retval
if isPreinstall == False:
i = 0
validTasks = []
try:
log("Info: Preparing for Rollback for all the valid tasks.")
while i < len(plist):
if taskApplicable(plist[i],shouldUpdateBrowserplugin,shouldUpdatePrintAutomator,shouldUpdatePrintPDFServices) == True and checkLanguage(plist[i],langObtained) == True:
validTasks.append(i)
prepareForRollback(plist[i],i)
i = i + 1
log("Info: Rollback preparation successful.")
except Exception, err:
log(("EXCEPTION: Caught an exception while preparing for rollback for task %d.") %(i))
failedTask = i
printStacktrace(traceback.format_exc())
if CLI_mode == "0":
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + 'GenericError'
retval = errorCodeDict["GenericError"]
try:
runCmd(cmdline, True)
except Exception, e:
log("ERROR: Fail to show UI alert.")
if e != '':
log(e)
printTaskDetails(plist[failedTask])
return retval
try:
j = 0
numberOfRollbacks = 0
log("Info: Executing all valid tasks.")
while j < len(validTasks):
i = validTasks[j]
numberOfRollbacks = j
if (plist[i]['TaskType'] == 'Remove'):
log((" Executing task %d : %s : %s.") %(i, plist[i]["TaskType"], plist[i]["RelativePath"]))
else:
perms = plist[i]["FileAttributes"]["NSFilePosixPermissions"]
perms = ("%o") %(perms)
log((" Executing task %d : %s : %s : %s.") %(i, plist[i]["TaskType"], plist[i]["RelativePath"], perms))
doOperation(plist[i])
j = j + 1
log("Info: Executed all valid tasks successfully.")
log("Info: Validating executed tasks.")
j = 0
while j < len(validTasks):
i = validTasks[j]
validatePostCondition(plist[i])
j = j + 1
log("Info: Validated executed tasks successfully.")
except Exception, err:
log(("EXCEPTION: Caught an exception while executing task %d.") %(i))
failedTask = i
printStacktrace(traceback.format_exc())
i = numberOfRollbacks
fatalError = False
failureFilePath = ''
log(("Info: Performing rollback operation. The number of rollbacks to perform - %d.") %(numberOfRollbacks + 1))
while i >= 0:
if (rollbackTask[i]['task'] == 'Remove'):
log((" Rolling back task %d : %s : %s.") %( rollbackTask[i]["tasknumber"], rollbackTask[i]["task"], rollbackTask[i]["path"]))
else:
log((" Rolling back task %d : %s : %s : %s.") %( rollbackTask[i]["tasknumber"], rollbackTask[i]["task"], rollbackTask[i]["path"], rollbackTask[i]["permissions"]))
didRollback = doRollback(rollbackTask[i])
i = i - 1
if didRollback == False:
fatalError = True
printTaskDetails(plist[rollbackTask[i]["tasknumber"]])
if fatalError == False:
log("Info: Rollback operation successful. Application state is restored.")
# Rollback is successful, now do processing for failed task
if not err.args[0] in errorCodeDict:
errorType = "GenericError"
else:
errorType = err.args[0]
if len(err.args) == 2:
failureFilePath = err.args[1]
log(("Info: Failure file path %s") %(failureFilePath))
retval = errorCodeDict[errorType]
if CLI_mode == "0":
if failureFilePath != '':
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + errorType + ' ' + '"' + failureFilePath + '"'
else:
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' ' + errorType
try:
runCmd(cmdline, True)
except Exception, e:
log("ERROR: Fail to show the UI alert.")
if e != '':
log(e)
else:
log("ERROR: Rollback operation has failed. Application state might be corrupted.")
retval = errorCodeDict["RollbackError"]
if CLI_mode == "0":
cmdline = sudoCmd + ' ' + binaryPath + ' ShowTaskError ' + title + ' RollbackError '
try:
runCmd(cmdline, True)
except Exception, e:
log("ERROR: Fail to show the UI alert.")
if e != '':
log(e)
printTaskDetails(plist[failedTask])
return retval
updatePDFSettingsPlist()
# Fix for bug #2955103, try to restore signing of AdobePDFViewerPlugin, being broken by the old style String files.
restoreSignatureForPlugin("/Library/Internet Plug-Ins/AdobePDFViewer.plugin")
return retval
if __name__=='__main__':
global enable_debug_logs
enable_debug_logs = 0
# For setting the LogLevel, run following command
# $ sudo defaults write "/Library/Application Support/Adobe/Acrobat/10.0/com.adobe.Acrobat.InstallerOverrides" AcroPatchLogLevel debug
log(" INFO: For creating debug logs, please run following command in terminal")
log(" $ sudo defaults write \"/Library/Application Support/Adobe/Acrobat/10.0/com.adobe.Acrobat.InstallerOverrides\" AcroPatchLogLevel debug")
# Get the LogLevel setting
defaults_cmd = '/usr/bin/defaults read \"/Library/Application Support/Adobe/Acrobat/10.0/com.adobe.Acrobat.InstallerOverrides" AcroPatchLogLevel'
(out,err,ret_code) = runCmd(defaults_cmd, True)
if( ret_code == 0):
out = out.strip()
out = out.upper()
if (out == 'DEBUG'):
enable_debug_logs = 1
log('Info: Debug logs are enabled')
isPreinstall = sys.argv[1]
log(("Info: App. path is obtained as %s.") %(appPath))
if isPreinstall == "1":
isPreinstall = True
killAdobeResourceSynchronizer()
else:
isPreinstall = False
retval = beginScan(isPreinstall)
sys.exit(retval)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment