diff options
-rw-r--r-- | chrome/browser/cocoa/keystone_glue.mm | 94 | ||||
-rwxr-xr-x | chrome/chrome.gyp | 10 | ||||
-rwxr-xr-x | chrome/tools/build/mac/keystone_install.sh | 100 | ||||
-rwxr-xr-x | chrome/tools/build/mac/keystone_install_test.sh | 11 |
4 files changed, 171 insertions, 44 deletions
diff --git a/chrome/browser/cocoa/keystone_glue.mm b/chrome/browser/cocoa/keystone_glue.mm index 3ef10bc..468eca90 100644 --- a/chrome/browser/cocoa/keystone_glue.mm +++ b/chrome/browser/cocoa/keystone_glue.mm @@ -32,6 +32,15 @@ typedef enum { kKSRegistrationDontKnowWhatKindOfTicket, } KSRegistrationTicketType; +NSString* KSRegistrationVersionKey = @"Version"; +NSString* KSRegistrationExistenceCheckerTypeKey = @"ExistenceCheckerType"; +NSString* KSRegistrationExistenceCheckerStringKey = @"ExistenceCheckerString"; +NSString* KSRegistrationServerURLStringKey = @"URLString"; +NSString* KSRegistrationPreserveTrustedTesterTokenKey = @"PreserveTTT"; +NSString* KSRegistrationTagKey = @"Tag"; +NSString* KSRegistrationTagPathKey = @"TagPath"; +NSString* KSRegistrationTagKeyKey = @"TagKey"; + NSString *KSRegistrationDidCompleteNotification = @"KSRegistrationDidCompleteNotification"; NSString *KSRegistrationPromotionDidCompleteNotification = @@ -40,7 +49,6 @@ NSString *KSRegistrationPromotionDidCompleteNotification = NSString *KSRegistrationCheckForUpdateNotification = @"KSRegistrationCheckForUpdateNotification"; NSString *KSRegistrationStatusKey = @"Status"; -NSString *KSRegistrationVersionKey = @"Version"; NSString *KSRegistrationUpdateCheckErrorKey = @"Error"; NSString *KSRegistrationStartUpdateNotification = @@ -57,20 +65,10 @@ NSString *KSRegistrationRemoveExistingTag = @""; + (id)registrationWithProductID:(NSString*)productID; -- (BOOL)registerWithVersion:(NSString*)version - existenceCheckerType:(KSExistenceCheckerType)xctype - existenceCheckerString:(NSString*)xc - serverURLString:(NSString*)serverURLString - preserveTTToken:(BOOL)preserveToken - tag:(NSString*)tag; - -- (BOOL)promoteWithVersion:(NSString*)version - existenceCheckerType:(KSExistenceCheckerType)xctype - existenceCheckerString:(NSString*)xc - serverURLString:(NSString*)serverURLString - preserveTTToken:(BOOL)preserveToken - tag:(NSString*)tag - authorization:(AuthorizationRef)authorization; +- (BOOL)registerWithParameters:(NSDictionary*)args; + +- (BOOL)promoteWithParameters:(NSDictionary*)args + authorization:(AuthorizationRef)authorization; - (void)setActive; - (void)checkForUpdate; @@ -81,6 +79,14 @@ NSString *KSRegistrationRemoveExistingTag = @""; @interface KeystoneGlue(Private) +// Returns the path to the application's Info.plist file. This returns the +// outer application bundle's Info.plist, not the framework's Info.plist. +- (NSString*)appInfoPlistPath; + +// Returns a dictionary containing parameters to be used for a KSRegistration +// -registerWithParameters: or -promoteWithParameters:authorization: call. +- (NSDictionary*)keystoneParameters; + // Called when Keystone registration completes. - (void)registrationComplete:(NSNotification*)notification; @@ -145,6 +151,12 @@ const NSString* const kAutoupdateStatusNotification = const NSString* const kAutoupdateStatusStatus = @"status"; const NSString* const kAutoupdateStatusVersion = @"version"; +namespace { + +const NSString* const kChannelKey = @"KSChannelID"; + +} // namespace + @implementation KeystoneGlue + (id)defaultKeystoneGlue { @@ -233,7 +245,7 @@ const NSString* const kAutoupdateStatusVersion = @"version"; return; } - NSString* channel = [infoDictionary objectForKey:@"KSChannelID"]; + NSString* channel = [infoDictionary objectForKey:kChannelKey]; // The stable channel has no tag. If updating to stable, remove the // dev and beta tags since we've been "promoted". if (channel == nil) @@ -268,15 +280,35 @@ const NSString* const kAutoupdateStatusVersion = @"version"; return YES; } +- (NSString*)appInfoPlistPath { + // NSBundle ought to have a way to access this path directly, but it + // doesn't. + return [[appPath_ stringByAppendingPathComponent:@"Contents"] + stringByAppendingPathComponent:@"Info.plist"]; +} + +- (NSDictionary*)keystoneParameters { + NSNumber* xcType = [NSNumber numberWithInt:kKSPathExistenceChecker]; + NSNumber* preserveTTToken = [NSNumber numberWithBool:YES]; + NSString* tagPath = [self appInfoPlistPath]; + + return [NSDictionary dictionaryWithObjectsAndKeys: + version_, KSRegistrationVersionKey, + xcType, KSRegistrationExistenceCheckerTypeKey, + appPath_, KSRegistrationExistenceCheckerStringKey, + url_, KSRegistrationServerURLStringKey, + preserveTTToken, KSRegistrationPreserveTrustedTesterTokenKey, + channel_, KSRegistrationTagKey, + tagPath, KSRegistrationTagPathKey, + kChannelKey, KSRegistrationTagKeyKey, + nil]; +} + - (void)registerWithKeystone { [self updateStatus:kAutoupdateRegistering version:nil]; - if (![registration_ registerWithVersion:version_ - existenceCheckerType:kKSPathExistenceChecker - existenceCheckerString:appPath_ - serverURLString:url_ - preserveTTToken:YES - tag:channel_]) { + NSDictionary* parameters = [self keystoneParameters]; + if (![registration_ registerWithParameters:parameters]) { [self updateStatus:kAutoupdateRegisterFailed version:nil]; return; } @@ -398,9 +430,7 @@ const NSString* const kAutoupdateStatusVersion = @"version"; - (void)determineUpdateStatus { DCHECK(![NSThread isMainThread]); - NSString* appInfoPlistPath = - [[appPath_ stringByAppendingPathComponent:@"Contents"] - stringByAppendingPathComponent:@"Info.plist"]; + NSString* appInfoPlistPath = [self appInfoPlistPath]; NSDictionary* infoPlist = [NSDictionary dictionaryWithContentsOfFile:appInfoPlistPath]; NSString* version = [infoPlist objectForKey:@"CFBundleShortVersionString"]; @@ -609,8 +639,8 @@ const NSString* const kAutoupdateStatusVersion = @"version"; // causes http://b/2289908, which this workaround addresses. // // This is run synchronously, which isn't optimal, but - // -[KSRegistration promoteWithVersion:...] is currently synchronous too, - // and this operation needs to happen before that one. + // -[KSRegistration promoteWithParameters:authorization:] is currently + // synchronous too, and this operation needs to happen before that one. // // TODO(mark): Make asynchronous. That only makes sense if the promotion // operation itself is asynchronous too. http://b/2290009. Hopefully, @@ -647,13 +677,9 @@ const NSString* const kAutoupdateStatusVersion = @"version"; // call. authorization_.swap(authorization); - if (![registration_ promoteWithVersion:version_ - existenceCheckerType:kKSPathExistenceChecker - existenceCheckerString:appPath_ - serverURLString:url_ - preserveTTToken:YES - tag:channel_ - authorization:authorization_]) { + NSDictionary* parameters = [self keystoneParameters]; + if (![registration_ promoteWithParameters:parameters + authorization:authorization_]) { [self updateStatus:kAutoupdatePromoteFailed version:nil]; authorization_.reset(); return; diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 440b610..a7a0e13 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -3827,13 +3827,15 @@ { # Modify the Info.plist as needed. The script explains why this # is needed. This is also done in the helper_app and chrome_dll - # targets. Use -b0 and -k0 to not include any Breakpad or - # Keystone information; that all goes into the framework's - # Info.plist. Use -s1 to include Subversion information. + # targets. Use -b0 to not include any Breakpad information; that + # all goes into the framework's Info.plist. Keystone information + # is included if Keystone is enabled because the ticket will + # reference this Info.plist to determine the tag of the installed + # product. Use -s1 to include Subversion information. 'postbuild_name': 'Tweak Info.plist', 'action': ['<(tweak_info_plist_path)', '-b0', - '-k0', + '-k<(mac_keystone)', '-s1', '<(branding)', '<(mac_bundle_id)'], diff --git a/chrome/tools/build/mac/keystone_install.sh b/chrome/tools/build/mac/keystone_install.sh index e4fd4cb..67e3a78 100755 --- a/chrome/tools/build/mac/keystone_install.sh +++ b/chrome/tools/build/mac/keystone_install.sh @@ -123,6 +123,79 @@ function ensure_writable_symlink() { return 0 } +# Prints the version of ksadmin, as reported by ksadmin --ksadmin-version, to +# stdout. This function operates with "static" variables: it will only check +# the ksadmin version once per script run. If ksadmin is old enough to not +# support --ksadmin-version, or another error occurs, this function prints an +# empty string. +G_CHECKED_KSADMIN_VERSION= +G_KSADMIN_VERSION= +function ksadmin_version() { + if [ -z "${G_CHECKED_KSADMIN_VERSION}" ] ; then + G_CHECKED_KSADMIN_VERSION=1 + G_KSADMIN_VERSION=$(ksadmin --ksadmin-version || true) + fi + echo "${G_KSADMIN_VERSION}" + return 0 +} + +# Compares the installed ksadmin version against a supplied version number, +# and returns 0 (true) if the number to check is the same as or newer than the +# installed Keystone. Returns 1 (false) if the installed Keystone version +# number cannot be determined or if the number to check is less than the +# installed Keystone. The check argument should be a string of the form +# "major.minor.micro.build". +function is_ksadmin_version_ge() { + CHECK_VERSION=${1} + KSADMIN_VERSION=$(ksadmin_version) + if [ -n "${KSADMIN_VERSION}" ] ; then + VER_RE='^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$' + + KSADMIN_VERSION_MAJOR=$(sed -Ene "s/${VER_RE}/\1/p" <<< ${KSADMIN_VERSION}) + KSADMIN_VERSION_MINOR=$(sed -Ene "s/${VER_RE}/\2/p" <<< ${KSADMIN_VERSION}) + KSADMIN_VERSION_MICRO=$(sed -Ene "s/${VER_RE}/\3/p" <<< ${KSADMIN_VERSION}) + KSADMIN_VERSION_BUILD=$(sed -Ene "s/${VER_RE}/\4/p" <<< ${KSADMIN_VERSION}) + + CHECK_VERSION_MAJOR=$(sed -Ene "s/${VER_RE}/\1/p" <<< ${CHECK_VERSION}) + CHECK_VERSION_MINOR=$(sed -Ene "s/${VER_RE}/\2/p" <<< ${CHECK_VERSION}) + CHECK_VERSION_MICRO=$(sed -Ene "s/${VER_RE}/\3/p" <<< ${CHECK_VERSION}) + CHECK_VERSION_BUILD=$(sed -Ene "s/${VER_RE}/\4/p" <<< ${CHECK_VERSION}) + + if [ ${KSADMIN_VERSION_MAJOR} -gt ${CHECK_VERSION_MAJOR} ] || + ([ ${KSADMIN_VERSION_MAJOR} -eq ${CHECK_VERSION_MAJOR} ] && ( + [ ${KSADMIN_VERSION_MINOR} -gt ${CHECK_VERSION_MINOR} ] || + ([ ${KSADMIN_VERSION_MINOR} -eq ${CHECK_VERSION_MINOR} ] && ( + [ ${KSADMIN_VERSION_MICRO} -gt ${CHECK_VERSION_MICRO} ] || + ([ ${KSADMIN_VERSION_MICRO} -eq ${CHECK_VERSION_MICRO} ] && + [ ${KSADMIN_VERSION_BUILD} -ge ${CHECK_VERSION_BUILD} ]) + )) + )) ; then + return 0 + fi + fi + + return 1 +} + +# Returns 0 (true) if ksadmin supports --tag. +function ksadmin_supports_tag() { + KSADMIN_VERSION=$(ksadmin_version) + if [ -n "${KSADMIN_VERSION}" ] ; then + # A ksadmin that recognizes --ksadmin-version and provides a version + # number is new enough to recognize --tag. + return 0 + fi + return 1 +} + +# Returns 0 (true) if ksadmin supports --tag-path and --tag-key. +function ksadmin_supports_tagpath_tagkey() { + # --tag-path and --tag-key were introduced in Keystone 1.0.7.1306. + is_ksadmin_version_ge 1.0.7.1306 + # The return value of is_ksadmin_version_ge is used as this function's + # return value. +} + # The argument should be the disk image path. Make sure it exists. if [ $# -lt 1 ] || [ ! -d "${1}" ]; then exit 2 @@ -325,7 +398,9 @@ NEW_VERSION_KS=$(defaults read "${NEW_KS_PLIST}" "${KS_VERSION_KEY}" || exit 9) URL=$(defaults read "${NEW_KS_PLIST}" KSUpdateURL || exit 9) # The channel ID is optional. Suppress stderr to prevent Keystone from seeing # possible error output. -CHANNEL_ID=$(defaults read "${NEW_KS_PLIST}" KSChannelID 2>/dev/null || true) +CHANNEL_ID_KEY=KSChannelID +CHANNEL_ID=$(defaults read "${NEW_KS_PLIST}" "${CHANNEL_ID_KEY}" 2>/dev/null || + true) # Make sure that the update was successful by comparing the version found in # the update with the version now on disk. @@ -337,10 +412,25 @@ fi # lsregister's exit codes shouldn't be confused with this script's own. /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister "${DEST}" || true +# Call ksadmin_version once to prime the global state. This is needed because +# subsequent calls to ksadmin_version that occur in $(...) expansions will not +# affect the global state (although they can read from the already-initialized +# global state) and thus will cause a new ksadmin --ksadmin-version process to +# run for each check unless the globals have been properly initialized +# beforehand. +ksadmin_version >& /dev/null || true + # Notify Keystone. -KSADMIN_VERSION=$(ksadmin --ksadmin-version || true) -if [ -n "${KSADMIN_VERSION}" ] ; then - # If ksadmin recognizes --ksadmin-version, it will recognize --tag. +if ksadmin_supports_tagpath_tagkey ; then + ksadmin --register \ + -P "${PRODUCT_ID}" \ + --version "${NEW_VERSION_KS}" \ + --xcpath "${DEST}" \ + --url "${URL}" \ + --tag "${CHANNEL_ID}" \ + --tag-path "${DEST}/Contents/Info.plist" \ + --tag-key "${CHANNEL_ID_KEY}" || exit 11 +elif ksadmin_supports_tag ; then ksadmin --register \ -P "${PRODUCT_ID}" \ --version "${NEW_VERSION_KS}" \ @@ -348,8 +438,6 @@ if [ -n "${KSADMIN_VERSION}" ] ; then --url "${URL}" \ --tag "${CHANNEL_ID}" || exit 11 else - # Older versions of ksadmin don't recognize --tag. The application will - # set the tag when it runs. ksadmin --register \ -P "${PRODUCT_ID}" \ --version "${NEW_VERSION_KS}" \ diff --git a/chrome/tools/build/mac/keystone_install_test.sh b/chrome/tools/build/mac/keystone_install_test.sh index 3b69de7..32930bf 100755 --- a/chrome/tools/build/mac/keystone_install_test.sh +++ b/chrome/tools/build/mac/keystone_install_test.sh @@ -22,6 +22,9 @@ PRODNAME="Google Chrome" APPNAME="${PRODNAME}.app" FWKNAME="${PRODNAME} Framework.framework" +# The version number for fake ksadmin to pretend to be +KSADMIN_VERSION_LIE="1.0.7.1306" + # Temp directory to be used as the disk image (source) TEMPDIR=$(mktemp -d -t $(basename ${0})) PATH=$PATH:"${TEMPDIR}" @@ -72,6 +75,10 @@ function make_old_dest() { defaults write "${DEST}/Contents/Info" KSVersion 0 cat >"${TEMPDIR}"/ksadmin <<EOF #!/bin/sh +if [ "\${1}" = "--ksadmin-version" ] ; then + echo "${KSADMIN_VERSION_LIE}" + exit 0 +fi if [ -z "\${FAKE_SYSTEM_TICKET}" ] && [ "\${1}" = "-S" ] ; then echo no system tix! >& 2 exit 1 @@ -93,6 +100,10 @@ function make_new_dest() { defaults write "${RSRCDIR}/Info" KSVersion 0 cat >"${TEMPDIR}"/ksadmin <<EOF #!/bin/sh +if [ "\${1}" = "--ksadmin-version" ] ; then + echo "${KSADMIN_VERSION_LIE}" + exit 0 +fi if [ -z "\${FAKE_SYSTEM_TICKET}" ] && [ "\${1}" = "-S" ] ; then echo no system tix! >& 2 exit 1 |