Chromium Code Reviews| Index: client/settings_mac.mm |
| diff --git a/client/settings_mac.mm b/client/settings_mac.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..394e843c18fd5c55b455bf5c34018a734eb0bed4 |
| --- /dev/null |
| +++ b/client/settings_mac.mm |
| @@ -0,0 +1,217 @@ |
| +// Copyright 2015 The Crashpad Authors. All rights reserved. |
| +// |
| +// Licensed under the Apache License, Version 2.0 (the "License"); |
| +// you may not use this file except in compliance with the License. |
| +// You may obtain a copy of the License at |
| +// |
| +// http://www.apache.org/licenses/LICENSE-2.0 |
| +// |
| +// Unless required by applicable law or agreed to in writing, software |
| +// distributed under the License is distributed on an "AS IS" BASIS, |
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| +// See the License for the specific language governing permissions and |
| +// limitations under the License. |
| + |
| +#import "client/settings_mac.h" |
| + |
| +#include <fcntl.h> |
| +#include <unistd.h> |
| +#include <uuid/uuid.h> |
| + |
| +#include "base/logging.h" |
| +#include "base/posix/eintr_wrapper.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#include "util/misc/uuid.h" |
| + |
| +namespace crashpad { |
| +namespace internal { |
| + |
| +namespace { |
| + |
| +NSString* const kClientID = @"ClientID"; |
| +NSString* const kUploadsEnabled = @"UploadsEnabled"; |
| +NSString* const kLastUploadAttemptTime = @"LastUploadAttemptTime"; |
| + |
| +} // namespace |
| + |
| +SettingsMac::SettingsMac(const base::FilePath& prefs_file) |
| + : prefs_file_(prefs_file) { |
| +} |
| + |
| +SettingsMac::~SettingsMac() { |
| +} |
| + |
| +bool SettingsMac::Initialize() { |
| + ScopedFileHandle fd(HANDLE_EINTR( |
| + open(prefs_file(), |
| + O_CREAT|O_EXCL|O_EXLOCK|O_WRONLY, |
|
Mark Mentovai
2015/03/08 05:21:56
Spaces around the |s, everywhere you use bitwise O
Robert Sesek
2015/03/08 22:15:36
Done.
|
| + 0644))); |
| + |
| + // The file was created, so this is a new database that needs to be |
| + // initialized with a client ID. |
| + if (fd.is_valid()) { |
| + uuid_t client_id; |
| + uuid_generate(client_id); |
| + |
| + std::string client_id_string(UUID(client_id).ToString()); |
| + |
| + NSDictionary* plist = |
| + @{ kClientID : base::SysUTF8ToNSString(client_id_string) }; |
| + |
| + return WritePlist(fd.get(), plist); |
| + } |
| + |
| + // The file wasn't opened successfully, try opening it without creating. |
| + fd.reset(HANDLE_EINTR(open(prefs_file(), O_RDONLY|O_SHLOCK))); |
| + PLOG_IF(ERROR, !fd.is_valid()) << "open " << prefs_file(); |
| + return fd.is_valid(); |
| +} |
| + |
| +bool SettingsMac::GetClientID(std::string* client_id) { |
|
Mark Mentovai
2015/03/08 05:21:56
These are going to be called from weird places tha
Robert Sesek
2015/03/08 22:15:36
Done. I didn't know the context of when these woul
|
| + base::scoped_nsobject<id> value = GetSetting(kClientID); |
| + if (!value) |
|
Mark Mentovai
2015/03/08 05:21:56
If the plist couldn’t be read at any point, we sho
Robert Sesek
2015/03/08 22:15:36
Done.
Mark Mentovai
2015/03/09 14:01:23
Robert Sesek wrote:
|
| + return false; |
| + |
| + if (![value isKindOfClass:[NSString class]]) { |
|
Mark Mentovai
2015/03/08 05:21:56
Why not ObjCCast?
Robert Sesek
2015/03/08 22:15:36
I didn't see any advantage to using that because I
|
| + LOG(ERROR) << [kClientID UTF8String] << " is not a NSString"; |
| + return false; |
| + } |
| + |
| + *client_id = base::SysNSStringToUTF8(value); |
| + return true; |
| +} |
| + |
| +bool SettingsMac::GetUploadsEnabled(bool* enabled) { |
| + base::scoped_nsobject<id> value = GetSetting(kUploadsEnabled); |
| + if (!value) |
| + return false; |
| + |
| + if (![value isKindOfClass:[NSValue class]]) { |
| + LOG(ERROR) << [kUploadsEnabled UTF8String] << " is not a NSValue"; |
| + return false; |
| + } |
| + |
| + *enabled = [value boolValue]; |
| + return true; |
|
Mark Mentovai
2015/03/08 05:21:56
With this implementation, a fresh database will re
Robert Sesek
2015/03/08 22:15:36
Done.
|
| +} |
| + |
| +bool SettingsMac::SetUploadsEnabled(bool enabled) { |
| + return SetSetting(kUploadsEnabled, [NSNumber numberWithBool:enabled]); |
| +} |
| + |
| +bool SettingsMac::GetLastUploadAttemptTime(time_t* time) { |
| + base::scoped_nsobject<id> value = GetSetting(kLastUploadAttemptTime); |
| + if (!value) |
| + return false; |
| + |
| + if (![value isKindOfClass:[NSValue class]]) { |
| + LOG(ERROR) << [kLastUploadAttemptTime UTF8String] << " is not a NSValue"; |
| + return false; |
| + } |
| + |
| + *time = [value doubleValue]; |
| + return true; |
| +} |
| + |
| +bool SettingsMac::SetLastUploadAttemptTime(time_t time) { |
| + return SetSetting(kLastUploadAttemptTime, [NSNumber numberWithDouble:time]); |
| +} |
| + |
| +base::scoped_nsobject<NSMutableDictionary> SettingsMac::ReadPlist( |
| + FileHandle fd) { |
| + base::scoped_nsobject<NSMutableDictionary> plist; |
| + |
| + base::scoped_nsobject<NSFileHandle> file_handle( |
| + [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]); |
| + NSData* data = nil; |
| + @try { |
| + data = [file_handle readDataToEndOfFile]; |
| + } @catch (NSException* exception) { |
| + LOG(ERROR) << "Failed to read settings: " << [exception description]; |
| + return plist; |
| + } |
| + if (!data) |
| + return plist; |
| + |
| + NSError* error = nil; |
| + NSPropertyListMutabilityOptions options = NSPropertyListMutableContainers; |
| + plist.reset( |
| + [[NSPropertyListSerialization propertyListWithData:data |
| + options:options |
| + format:nullptr |
| + error:&error] retain]); |
| + LOG_IF(ERROR, error != nil) << "Failed to deserialize settings: " |
| + << [error description]; |
| + return plist; |
| +} |
| + |
| +bool SettingsMac::WritePlist(FileHandle fd, NSDictionary* plist) { |
| + NSError* error = nil; |
| + NSPropertyListFormat format = NSPropertyListXMLFormat_v1_0; |
|
Mark Mentovai
2015/03/08 05:21:56
Not the faster binary format?
Robert Sesek
2015/03/08 22:15:36
Done. The Foundation guides don't actually provide
|
| + NSData* data = [NSPropertyListSerialization dataWithPropertyList:plist |
| + format:format |
| + options:0 |
| + error:&error]; |
| + LOG_IF(ERROR, error != nil) << "Failed to serialize settings: " |
| + << [error description]; |
| + if (!data) |
| + return false; |
| + |
| + if (HANDLE_EINTR(ftruncate(fd, 0)) != 0) { |
| + PLOG(ERROR) << "ftruncate failed on settings file"; |
| + return false; |
| + } |
| + |
| + if (LoggingSeekFile(fd, 0, SEEK_SET) != 0) { |
| + return false; |
| + } |
| + |
| + base::scoped_nsobject<NSFileHandle> file_handle( |
| + [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]); |
| + @try { |
| + [file_handle writeData:data]; |
| + } @catch (NSException* exception) { |
| + LOG(ERROR) << "Failed to write settings: " << [exception description]; |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +base::scoped_nsobject<id> SettingsMac::GetSetting(NSString* key) { |
| + base::scoped_nsobject<id> value; |
|
Mark Mentovai
2015/03/08 05:21:56
You don’t need to declare this ’til much later, if
Robert Sesek
2015/03/08 22:15:36
I dislike having to read and write such a verbose
|
| + |
| + ScopedFileHandle fd(HANDLE_EINTR(open(prefs_file(), O_RDONLY|O_SHLOCK))); |
| + if (!fd.is_valid()) { |
| + PLOG(ERROR) << "open for get setting"; |
| + return value; |
| + } |
| + |
| + base::scoped_nsobject<NSMutableDictionary> plist(ReadPlist(fd.get())); |
| + if (!plist.get()) |
| + return value; |
| + |
| + value.reset([[plist objectForKey:key] retain]); |
| + return value; |
| +} |
| + |
| +bool SettingsMac::SetSetting(NSString* key, NSObject* value) { |
| + base::scoped_nsobject<NSMutableDictionary> plist; |
| + |
| + ScopedFileHandle fd(HANDLE_EINTR(open(prefs_file(), O_RDWR|O_EXLOCK))); |
| + if (!fd.is_valid()) { |
| + LOG(ERROR) << "open for set setting"; |
|
Mark Mentovai
2015/03/08 05:21:56
PLOG
Robert Sesek
2015/03/08 22:15:36
Done.
|
| + return plist; |
| + } |
| + |
| + plist = ReadPlist(fd.get()); |
| + if (!plist.get()) |
| + return false; |
| + |
| + [plist setObject:value forKey:key]; |
| + |
| + return WritePlist(fd.get(), plist); |
| +} |
| + |
| +} // namespace internal |
| +} // namespace crashpad |