Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/quarantine.h" | 5 #include "content/browser/download/quarantine.h" |
| 6 | 6 |
| 7 #include <ApplicationServices/ApplicationServices.h> | 7 #import <ApplicationServices/ApplicationServices.h> |
| 8 #include <Foundation/Foundation.h> | 8 #import <Foundation/Foundation.h> |
| 9 | 9 |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/mac/foundation_util.h" | 12 #include "base/mac/foundation_util.h" |
| 13 #include "base/mac/mac_logging.h" | 13 #include "base/mac/mac_logging.h" |
| 14 #include "base/mac/mac_util.h" | 14 #include "base/mac/mac_util.h" |
| 15 #include "base/mac/scoped_cftyperef.h" | 15 #include "base/mac/scoped_cftyperef.h" |
| 16 #include "base/mac/scoped_nsobject.h" | |
| 17 #include "base/strings/sys_string_conversions.h" | |
| 16 #include "base/threading/thread_restrictions.h" | 18 #include "base/threading/thread_restrictions.h" |
| 17 #include "url/gurl.h" | 19 #include "url/gurl.h" |
| 18 | 20 |
| 21 namespace { | |
| 22 | |
| 23 // Once Chrome no longer supports macOS 10.9, this code will no longer be | |
| 24 // necessary. Note that LSCopyItemAttribute was deprecated in macOS 10.8, but | |
| 25 // the replacement to kLSItemQuarantineProperties did not exist until macOS | |
| 26 // 10.10. | |
| 27 #if !defined(MAC_OS_X_VERSION_10_10) || \ | |
| 28 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 | |
| 29 #pragma clang diagnostic push | |
| 30 #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 31 bool GetQuarantinePropertiesDeprecated( | |
| 32 const base::FilePath& file, | |
| 33 base::scoped_nsobject<NSMutableDictionary>* properties) { | |
| 34 FSRef file_ref; | |
| 35 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) | |
| 36 return false; | |
| 37 CFTypeRef quarantine_properties_base = NULL; | |
|
Avi (use Gerrit)
2016/09/28 02:32:13
Can you do a ScopedCFTypeRef here and use Initiali
erikchen
2016/09/28 19:59:00
Done.
Note that this was copy-pasted from the pr
| |
| 38 bool success = true; | |
| 39 if (LSCopyItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, | |
| 40 &quarantine_properties_base) == noErr) { | |
| 41 if (CFGetTypeID(quarantine_properties_base) == CFDictionaryGetTypeID()) { | |
|
Avi (use Gerrit)
2016/09/28 02:32:14
CFCast<>
erikchen
2016/09/28 19:59:00
Done.
| |
| 42 // Quarantine properties will already exist if LSFileQuarantineEnabled | |
| 43 // is on and the file doesn't match an exclusion. | |
| 44 properties->reset( | |
| 45 [(NSDictionary*)quarantine_properties_base mutableCopy]); | |
|
Avi (use Gerrit)
2016/09/28 02:32:14
CFToNSCast
erikchen
2016/09/28 19:59:00
Done.
| |
| 46 } else { | |
| 47 LOG(WARNING) << "kLSItemQuarantineProperties is not a dictionary on file " | |
| 48 << file.value(); | |
| 49 success = false; | |
| 50 } | |
| 51 CFRelease(quarantine_properties_base); | |
|
Avi (use Gerrit)
2016/09/28 02:32:13
(then you wouldn't need this)
erikchen
2016/09/28 19:59:00
Done.
| |
| 52 } | |
| 53 return success; | |
| 54 } | |
| 55 | |
| 56 bool SetQuarantinePropertiesDeprecated(const base::FilePath& file, | |
| 57 NSDictionary* properties) { | |
| 58 FSRef file_ref; | |
| 59 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) | |
| 60 return false; | |
| 61 OSStatus os_error = LSSetItemAttribute( | |
| 62 &file_ref, kLSRolesAll, kLSItemQuarantineProperties, properties); | |
| 63 if (os_error != noErr) { | |
| 64 OSSTATUS_LOG(WARNING, os_error) | |
| 65 << "Unable to set quarantine attributes on file " << file.value(); | |
| 66 return false; | |
| 67 } | |
| 68 return true; | |
| 69 } | |
| 70 #pragma clang diagnostic pop | |
| 71 #endif | |
| 72 | |
| 73 bool GetQuarantineProperties( | |
| 74 const base::FilePath& file, | |
| 75 base::scoped_nsobject<NSMutableDictionary>* properties) { | |
| 76 base::scoped_nsobject<NSURL> file_url([[NSURL alloc] | |
| 77 initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); | |
| 78 if (!file_url) | |
| 79 return false; | |
| 80 | |
| 81 // NSURLQuarantinePropertiesKey is only available on macOS 10.10+. | |
| 82 #pragma clang diagnostic push | |
| 83 #pragma clang diagnostic ignored "-Wunguarded-availability" | |
| 84 NSError* error = nil; | |
| 85 NSDictionary* quarantine_properties = nil; | |
| 86 BOOL success = [file_url getResourceValue:&quarantine_properties | |
| 87 forKey:NSURLQuarantinePropertiesKey | |
| 88 error:&error]; | |
| 89 #pragma clang diagnostic pop | |
| 90 if (!success) { | |
| 91 std::string error_message(error ? error.description.UTF8String : ""); | |
| 92 LOG(WARNING) << "Unable to get quarantine attributes for file " | |
| 93 << file.value() << ". Error: " << error_message; | |
| 94 return false; | |
| 95 } | |
| 96 | |
| 97 if (!quarantine_properties) | |
| 98 return true; | |
| 99 | |
| 100 if (![quarantine_properties isKindOfClass:[NSDictionary class]]) { | |
|
Avi (use Gerrit)
2016/09/28 02:32:14
Rather than using an NSDictionary* up there, use a
erikchen
2016/09/28 19:59:00
Done.
| |
| 101 LOG(WARNING) << "Quarantine properties have wrong class: " | |
| 102 << [[[quarantine_properties class] description] UTF8String]; | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 properties->reset([quarantine_properties mutableCopy]); | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 bool SetQuarantineProperties(const base::FilePath& file, | |
| 111 NSDictionary* properties) { | |
| 112 base::scoped_nsobject<NSURL> file_url([[NSURL alloc] | |
| 113 initFileURLWithPath:base::SysUTF8ToNSString(file.value())]); | |
| 114 if (!file_url) | |
| 115 return false; | |
| 116 | |
| 117 // NSURLQuarantinePropertiesKey is only available on macOS 10.10+. | |
| 118 #pragma clang diagnostic push | |
| 119 #pragma clang diagnostic ignored "-Wunguarded-availability" | |
| 120 NSError* error = nil; | |
| 121 bool success = [file_url setResourceValue:properties | |
| 122 forKey:NSURLQuarantinePropertiesKey | |
| 123 error:&error]; | |
| 124 #pragma clang diagnostic pop | |
| 125 if (!success) { | |
| 126 std::string error_message(error ? error.description.UTF8String : ""); | |
| 127 LOG(WARNING) << "Unable to set quarantine attributes on file " | |
| 128 << file.value() << ". Error: " << error_message; | |
| 129 return false; | |
| 130 } | |
| 131 return true; | |
| 132 } | |
| 133 | |
| 134 } // namespace | |
| 135 | |
| 19 namespace content { | 136 namespace content { |
| 20 | 137 |
| 21 namespace { | 138 namespace { |
| 22 | 139 |
| 23 // As of Mac OS X 10.4 ("Tiger"), files can be tagged with metadata describing | 140 // As of Mac OS X 10.4 ("Tiger"), files can be tagged with metadata describing |
| 24 // various attributes. Metadata is integrated with the system's Spotlight | 141 // various attributes. Metadata is integrated with the system's Spotlight |
| 25 // feature and is searchable. Ordinarily, metadata can only be set by | 142 // feature and is searchable. Ordinarily, metadata can only be set by |
| 26 // Spotlight importers, which requires that the importer own the target file. | 143 // Spotlight importers, which requires that the importer own the target file. |
| 27 // However, there's an attribute intended to describe the origin of a | 144 // However, there's an attribute intended to describe the origin of a |
| 28 // file, that can store the source URL and referrer of a downloaded file. | 145 // file, that can store the source URL and referrer of a downloaded file. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 104 // the URL the user initiated the download from. | 221 // the URL the user initiated the download from. |
| 105 | 222 |
| 106 // The OS will automatically quarantine files due to the | 223 // The OS will automatically quarantine files due to the |
| 107 // LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively | 224 // LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively |
| 108 // little about the files. We add more information about the download to | 225 // little about the files. We add more information about the download to |
| 109 // improve the UI shown by the OS when the users tries to open the file. | 226 // improve the UI shown by the OS when the users tries to open the file. |
| 110 bool AddQuarantineMetadataToFile(const base::FilePath& file, | 227 bool AddQuarantineMetadataToFile(const base::FilePath& file, |
| 111 const GURL& source, | 228 const GURL& source, |
| 112 const GURL& referrer) { | 229 const GURL& referrer) { |
| 113 base::ThreadRestrictions::AssertIOAllowed(); | 230 base::ThreadRestrictions::AssertIOAllowed(); |
| 114 FSRef file_ref; | 231 base::scoped_nsobject<NSMutableDictionary> properties; |
| 115 if (!base::mac::FSRefFromPath(file.value(), &file_ref)) | 232 bool success = false; |
| 233 if (base::mac::IsAtLeastOS10_10()) { | |
| 234 success = GetQuarantineProperties(file, &properties); | |
| 235 } else { | |
| 236 success = GetQuarantinePropertiesDeprecated(file, &properties); | |
| 237 } | |
| 238 | |
| 239 if (!success) | |
| 116 return false; | 240 return false; |
| 117 | 241 |
| 118 NSMutableDictionary* quarantine_properties = nil; | 242 if (!properties) { |
| 119 CFTypeRef quarantine_properties_base = NULL; | |
| 120 if (LSCopyItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, | |
| 121 &quarantine_properties_base) == noErr) { | |
| 122 if (CFGetTypeID(quarantine_properties_base) == CFDictionaryGetTypeID()) { | |
| 123 // Quarantine properties will already exist if LSFileQuarantineEnabled | |
| 124 // is on and the file doesn't match an exclusion. | |
| 125 quarantine_properties = | |
| 126 [[(NSDictionary*)quarantine_properties_base mutableCopy] autorelease]; | |
| 127 } else { | |
| 128 LOG(WARNING) << "kLSItemQuarantineProperties is not a dictionary on file " | |
| 129 << file.value(); | |
| 130 } | |
| 131 CFRelease(quarantine_properties_base); | |
| 132 } | |
| 133 | |
| 134 if (!quarantine_properties) { | |
| 135 // If there are no quarantine properties, then the file isn't quarantined | 243 // If there are no quarantine properties, then the file isn't quarantined |
| 136 // (e.g., because the user has set up exclusions for certain file types). | 244 // (e.g., because the user has set up exclusions for certain file types). |
| 137 // We don't want to add any metadata, because that will cause the file to | 245 // We don't want to add any metadata, because that will cause the file to |
| 138 // be quarantined against the user's wishes. | 246 // be quarantined against the user's wishes. |
| 139 return true; | 247 return true; |
| 140 } | 248 } |
| 141 | 249 |
| 142 // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and | 250 // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and |
| 143 // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only | 251 // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only |
| 144 // need to set the values that the OS can't infer. | 252 // need to set the values that the OS can't infer. |
| 145 | 253 |
| 146 if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { | 254 if (![properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { |
| 147 CFStringRef type = source.SchemeIsHTTPOrHTTPS() | 255 CFStringRef type = source.SchemeIsHTTPOrHTTPS() |
| 148 ? kLSQuarantineTypeWebDownload | 256 ? kLSQuarantineTypeWebDownload |
| 149 : kLSQuarantineTypeOtherDownload; | 257 : kLSQuarantineTypeOtherDownload; |
| 150 [quarantine_properties setValue:(NSString*)type | 258 [properties setValue:(NSString*)type |
| 151 forKey:(NSString*)kLSQuarantineTypeKey]; | 259 forKey:(NSString*)kLSQuarantineTypeKey]; |
| 152 } | 260 } |
| 153 | 261 |
| 154 if (![quarantine_properties | 262 if (![properties valueForKey:(NSString*)kLSQuarantineOriginURLKey] && |
| 155 valueForKey:(NSString*)kLSQuarantineOriginURLKey] && | |
| 156 referrer.is_valid()) { | 263 referrer.is_valid()) { |
| 157 NSString* referrer_url = | 264 NSString* referrer_url = |
| 158 [NSString stringWithUTF8String:referrer.spec().c_str()]; | 265 [NSString stringWithUTF8String:referrer.spec().c_str()]; |
| 159 [quarantine_properties setValue:referrer_url | 266 [properties setValue:referrer_url |
| 160 forKey:(NSString*)kLSQuarantineOriginURLKey]; | 267 forKey:(NSString*)kLSQuarantineOriginURLKey]; |
| 161 } | 268 } |
| 162 | 269 |
| 163 if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && | 270 if (![properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && |
| 164 source.is_valid()) { | 271 source.is_valid()) { |
| 165 NSString* origin_url = | 272 NSString* origin_url = |
| 166 [NSString stringWithUTF8String:source.spec().c_str()]; | 273 [NSString stringWithUTF8String:source.spec().c_str()]; |
| 167 [quarantine_properties setValue:origin_url | 274 [properties setValue:origin_url forKey:(NSString*)kLSQuarantineDataURLKey]; |
| 168 forKey:(NSString*)kLSQuarantineDataURLKey]; | |
| 169 } | 275 } |
| 170 | 276 |
| 171 OSStatus os_error = | 277 if (base::mac::IsAtLeastOS10_10()) { |
| 172 LSSetItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, | 278 return SetQuarantineProperties(file, properties); |
| 173 quarantine_properties); | 279 } else { |
| 174 if (os_error != noErr) { | 280 return SetQuarantinePropertiesDeprecated(file, properties); |
| 175 OSSTATUS_LOG(WARNING, os_error) | |
| 176 << "Unable to set quarantine attributes on file " << file.value(); | |
| 177 return false; | |
| 178 } | 281 } |
| 179 return true; | |
| 180 } | 282 } |
| 181 | 283 |
| 182 } // namespace | 284 } // namespace |
| 183 | 285 |
| 184 QuarantineFileResult QuarantineFile(const base::FilePath& file, | 286 QuarantineFileResult QuarantineFile(const base::FilePath& file, |
| 185 const GURL& source_url, | 287 const GURL& source_url, |
| 186 const GURL& referrer_url, | 288 const GURL& referrer_url, |
| 187 const std::string& client_guid) { | 289 const std::string& client_guid) { |
| 188 bool quarantine_succeeded = | 290 bool quarantine_succeeded = |
| 189 AddQuarantineMetadataToFile(file, source_url, referrer_url); | 291 AddQuarantineMetadataToFile(file, source_url, referrer_url); |
| 190 bool origin_succeeded = | 292 bool origin_succeeded = |
| 191 AddOriginMetadataToFile(file, source_url, referrer_url); | 293 AddOriginMetadataToFile(file, source_url, referrer_url); |
| 192 return quarantine_succeeded && origin_succeeded | 294 return quarantine_succeeded && origin_succeeded |
| 193 ? QuarantineFileResult::OK | 295 ? QuarantineFileResult::OK |
| 194 : QuarantineFileResult::ANNOTATION_FAILED; | 296 : QuarantineFileResult::ANNOTATION_FAILED; |
| 195 } | 297 } |
| 196 | 298 |
| 197 } // namespace content | 299 } // namespace content |
| OLD | NEW |