summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m755
1 files changed, 0 insertions, 755 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
deleted file mode 100644
index 88d26fb03..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m
+++ /dev/null
@@ -1,755 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#import "client/mac/sender/crash_report_sender.h"
-
-#import <Cocoa/Cocoa.h>
-#import <pwd.h>
-#import <sys/stat.h>
-#import <SystemConfiguration/SystemConfiguration.h>
-#import <unistd.h>
-
-#import "client/apple/Framework/BreakpadDefines.h"
-#import "common/mac/GTMLogger.h"
-#import "common/mac/HTTPMultipartUpload.h"
-
-
-#define kLastSubmission @"LastSubmission"
-const int kUserCommentsMaxLength = 1500;
-const int kEmailMaxLength = 64;
-
-#define kApplePrefsSyncExcludeAllKey \
- @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
-
-#pragma mark -
-
-@interface NSView (ResizabilityExtentions)
-// Shifts the view vertically by the given amount.
-- (void)breakpad_shiftVertically:(CGFloat)offset;
-
-// Shifts the view horizontally by the given amount.
-- (void)breakpad_shiftHorizontally:(CGFloat)offset;
-@end
-
-@implementation NSView (ResizabilityExtentions)
-- (void)breakpad_shiftVertically:(CGFloat)offset {
- NSPoint origin = [self frame].origin;
- origin.y += offset;
- [self setFrameOrigin:origin];
-}
-
-- (void)breakpad_shiftHorizontally:(CGFloat)offset {
- NSPoint origin = [self frame].origin;
- origin.x += offset;
- [self setFrameOrigin:origin];
-}
-@end
-
-@interface NSWindow (ResizabilityExtentions)
-// Adjusts the window height by heightDelta relative to its current height,
-// keeping all the content at the same size.
-- (void)breakpad_adjustHeight:(CGFloat)heightDelta;
-@end
-
-@implementation NSWindow (ResizabilityExtentions)
-- (void)breakpad_adjustHeight:(CGFloat)heightDelta {
- [[self contentView] setAutoresizesSubviews:NO];
-
- NSRect windowFrame = [self frame];
- windowFrame.size.height += heightDelta;
- [self setFrame:windowFrame display:YES];
- // For some reason the content view is resizing, but not adjusting its origin,
- // so correct it manually.
- [[self contentView] setFrameOrigin:NSMakePoint(0, 0)];
-
- [[self contentView] setAutoresizesSubviews:YES];
-}
-@end
-
-@interface NSTextField (ResizabilityExtentions)
-// Grows or shrinks the height of the field to the minimum required to show the
-// current text, preserving the existing width and origin.
-// Returns the change in height.
-- (CGFloat)breakpad_adjustHeightToFit;
-
-// Grows or shrinks the width of the field to the minimum required to show the
-// current text, preserving the existing height and origin.
-// Returns the change in width.
-- (CGFloat)breakpad_adjustWidthToFit;
-@end
-
-@implementation NSTextField (ResizabilityExtentions)
-- (CGFloat)breakpad_adjustHeightToFit {
- NSRect oldFrame = [self frame];
- // Starting with the 10.5 SDK, height won't grow, so make it huge to start.
- NSRect presizeFrame = oldFrame;
- presizeFrame.size.height = MAXFLOAT;
- // sizeToFit will blow out the width rather than making the field taller, so
- // we do it manually.
- NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame];
- NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
- NSWidth(oldFrame), newSize.height);
- [self setFrame:newFrame];
-
- return newSize.height - NSHeight(oldFrame);
-}
-
-- (CGFloat)breakpad_adjustWidthToFit {
- NSRect oldFrame = [self frame];
- [self sizeToFit];
- return NSWidth([self frame]) - NSWidth(oldFrame);
-}
-@end
-
-@interface NSButton (ResizabilityExtentions)
-// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
-// minimum width of 70, while preserving the right edge location.
-// Returns the change in width.
-- (CGFloat)breakpad_smartSizeToFit;
-@end
-
-@implementation NSButton (ResizabilityExtentions)
-- (CGFloat)breakpad_smartSizeToFit {
- NSRect oldFrame = [self frame];
- [self sizeToFit];
- NSRect newFrame = [self frame];
- // sizeToFit gives much worse results that IB's Size to Fit option. This is
- // the amount of padding IB adds over a sizeToFit, empirically determined.
- const float kExtraPaddingAmount = 12;
- const float kMinButtonWidth = 70; // The default button size in IB.
- newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount;
- if (NSWidth(newFrame) < kMinButtonWidth)
- newFrame.size.width = kMinButtonWidth;
- // Preserve the right edge location.
- newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame);
- [self setFrame:newFrame];
- return NSWidth(newFrame) - NSWidth(oldFrame);
-}
-@end
-
-#pragma mark -
-
-@interface Reporter(PrivateMethods)
-- (id)initWithConfigFile:(const char *)configFile;
-
-// Returns YES if it has been long enough since the last report that we should
-// submit a report for this crash.
-- (BOOL)reportIntervalElapsed;
-
-// Returns YES if we should send the report without asking the user first.
-- (BOOL)shouldSubmitSilently;
-
-// Returns YES if the minidump was generated on demand.
-- (BOOL)isOnDemand;
-
-// Returns YES if we should ask the user to provide comments.
-- (BOOL)shouldRequestComments;
-
-// Returns YES if we should ask the user to provide an email address.
-- (BOOL)shouldRequestEmail;
-
-// Shows UI to the user to ask for permission to send and any extra information
-// we've been instructed to request. Returns YES if the user allows the report
-// to be sent.
-- (BOOL)askUserPermissionToSend;
-
-// Returns the short description of the crash, suitable for use as a dialog
-// title (e.g., "The application Foo has quit unexpectedly").
-- (NSString*)shortDialogMessage;
-
-// Return explanatory text about the crash and the reporter, suitable for the
-// body text of a dialog.
-- (NSString*)explanatoryDialogText;
-
-// Returns the amount of time the UI should be shown before timing out.
-- (NSTimeInterval)messageTimeout;
-
-// Preps the comment-prompting alert window for display:
-// * localizes all the elements
-// * resizes and adjusts layout as necessary for localization
-// * removes the email section if includeEmail is NO
-- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail;
-
-// Rmevoes the email section of the dialog, adjusting the rest of the window
-// as necessary.
-- (void)removeEmailPrompt;
-
-// Run an alert window with the given timeout. Returns
-// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0
-// queues the message immediately in the modal run loop.
-- (NSInteger)runModalWindow:(NSWindow*)window
- withTimeout:(NSTimeInterval)timeout;
-
-// This method is used to periodically update the UI with how many
-// seconds are left in the dialog display.
-- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
-
-// When we receive this notification, it means that the user has
-// begun editing the email address or comments field, and we disable
-// the timers so that the user has as long as they want to type
-// in their comments/email.
-- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
-
-- (void)report;
-
-@end
-
-@implementation Reporter
-//=============================================================================
-- (id)initWithConfigFile:(const char *)configFile {
- if ((self = [super init])) {
- remainingDialogTime_ = 0;
- uploader_ = [[Uploader alloc] initWithConfigFile:configFile];
- if (!uploader_) {
- [self release];
- return nil;
- }
- }
- return self;
-}
-
-//=============================================================================
-- (BOOL)askUserPermissionToSend {
- // Initialize Cocoa, needed to display the alert
- NSApplicationLoad();
-
- // Get the timeout value for the notification.
- NSTimeInterval timeout = [self messageTimeout];
-
- NSInteger buttonPressed = NSAlertAlternateReturn;
- // Determine whether we should create a text box for user feedback.
- if ([self shouldRequestComments]) {
- BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
- if (!didLoadNib) {
- return NO;
- }
-
- [self configureAlertWindowIncludingEmail:[self shouldRequestEmail]];
-
- buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
-
- // Extract info from the user into the uploader_.
- if ([self commentsValue]) {
- [[uploader_ parameters] setObject:[self commentsValue]
- forKey:@BREAKPAD_COMMENTS];
- }
- if ([self emailValue]) {
- [[uploader_ parameters] setObject:[self emailValue]
- forKey:@BREAKPAD_EMAIL];
- }
- } else {
- // Create an alert panel to tell the user something happened
- NSPanel* alert =
- NSGetAlertPanel([self shortDialogMessage],
- @"%@",
- NSLocalizedString(@"sendReportButton", @""),
- NSLocalizedString(@"cancelButton", @""),
- nil,
- [self explanatoryDialogText]);
-
- // Pop the alert with an automatic timeout, and wait for the response
- buttonPressed = [self runModalWindow:alert withTimeout:timeout];
-
- // Release the panel memory
- NSReleaseAlertPanel(alert);
- }
- return buttonPressed == NSAlertDefaultReturn;
-}
-
-- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail {
- // Swap in localized values, making size adjustments to impacted elements as
- // we go. Remember that the origin is in the bottom left, so elements above
- // "fall" as text areas are shrunk from their overly-large IB sizes.
-
- // Localize the header. No resizing needed, as it has plenty of room.
- [dialogTitle_ setStringValue:[self shortDialogMessage]];
-
- // Localize the explanatory text field.
- [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
- [self explanatoryDialogText],
- NSLocalizedString(@"commentsMsg", @"")]];
- CGFloat commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
- [headerBox_ breakpad_shiftVertically:commentHeightDelta];
- [alertWindow_ breakpad_adjustHeight:commentHeightDelta];
-
- // Either localize the email explanation field or remove the whole email
- // section depending on whether or not we are asking for email.
- if (includeEmail) {
- [emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
- CGFloat emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
- [preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
- [alertWindow_ breakpad_adjustHeight:emailHeightDelta];
- } else {
- [self removeEmailPrompt]; // Handles necessary resizing.
- }
-
- // Localize the email label, and shift the associated text field.
- [emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
- CGFloat emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
- [emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
-
- // Localize the privacy policy label, and keep it right-aligned to the arrow.
- [privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
- CGFloat privacyLabelWidthDelta =
- [privacyLinkLabel_ breakpad_adjustWidthToFit];
- [privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
-
- // Ensure that the email field and the privacy policy link don't overlap.
- CGFloat kMinControlPadding = 8;
- CGFloat maxEmailFieldWidth = NSMinX([privacyLinkLabel_ frame]) -
- NSMinX([emailEntryField_ frame]) -
- kMinControlPadding;
- if (NSWidth([emailEntryField_ bounds]) > maxEmailFieldWidth &&
- maxEmailFieldWidth > 0) {
- NSSize emailSize = [emailEntryField_ frame].size;
- emailSize.width = maxEmailFieldWidth;
- [emailEntryField_ setFrameSize:emailSize];
- }
-
- // Localize the placeholder text.
- [[commentsEntryField_ cell]
- setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")];
- [[emailEntryField_ cell]
- setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")];
-
- // Localize the buttons, and keep the cancel button at the right distance.
- [sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
- CGFloat sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
- [cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
- [cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
- [cancelButton_ breakpad_smartSizeToFit];
-}
-
-- (void)removeEmailPrompt {
- [emailSectionBox_ setHidden:YES];
- CGFloat emailSectionHeight = NSHeight([emailSectionBox_ frame]);
- [preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
- [alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
-}
-
-- (NSInteger)runModalWindow:(NSWindow*)window
- withTimeout:(NSTimeInterval)timeout {
- // Queue a |stopModal| message to be performed in |timeout| seconds.
- if (timeout > 0.001) {
- remainingDialogTime_ = timeout;
- SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:);
- messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0
- target:self
- selector:updateSelector
- userInfo:nil
- repeats:YES];
- }
-
- // Run the window modally and wait for either a |stopModal| message or a
- // button click.
- [NSApp activateIgnoringOtherApps:YES];
- NSInteger returnMethod = [NSApp runModalForWindow:window];
-
- return returnMethod;
-}
-
-- (IBAction)sendReport:(id)sender {
- // Force the text fields to end editing so text for the currently focused
- // field will be commited.
- [alertWindow_ makeFirstResponder:alertWindow_];
-
- [alertWindow_ orderOut:self];
- // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
- // matches the AppKit function NSRunAlertPanel()
- [NSApp stopModalWithCode:NSAlertDefaultReturn];
-}
-
-// UI Button Actions
-//=============================================================================
-- (IBAction)cancel:(id)sender {
- [alertWindow_ orderOut:self];
- // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
- // matches the AppKit function NSRunAlertPanel()
- [NSApp stopModalWithCode:NSAlertAlternateReturn];
-}
-
-- (IBAction)showPrivacyPolicy:(id)sender {
- // Get the localized privacy policy URL and open it in the default browser.
- NSURL* privacyPolicyURL =
- [NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")];
- [[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
-}
-
-// Text Field Delegate Methods
-//=============================================================================
-- (BOOL) control:(NSControl*)control
- textView:(NSTextView*)textView
-doCommandBySelector:(SEL)commandSelector {
- BOOL result = NO;
- // If the user has entered text on the comment field, don't end
- // editing on "return".
- if (control == commentsEntryField_ &&
- commandSelector == @selector(insertNewline:)
- && [[textView string] length] > 0) {
- [textView insertNewlineIgnoringFieldEditor:self];
- result = YES;
- }
- return result;
-}
-
-- (void)controlTextDidBeginEditing:(NSNotification *)aNotification {
- [messageTimer_ invalidate];
- [self setCountdownMessage:@""];
-}
-
-- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer {
- remainingDialogTime_ -= 1;
-
- NSString *countdownMessage;
- NSString *formatString;
-
- int displayedTimeLeft; // This can be either minutes or seconds.
-
- if (remainingDialogTime_ > 59) {
- // calculate minutes remaining for UI purposes
- displayedTimeLeft = (int)(remainingDialogTime_ / 60);
-
- if (displayedTimeLeft == 1) {
- formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
- } else {
- formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
- }
- } else {
- displayedTimeLeft = (int)remainingDialogTime_;
- if (displayedTimeLeft == 1) {
- formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
- } else {
- formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
- }
- }
- countdownMessage = [NSString stringWithFormat:formatString,
- displayedTimeLeft];
- if (remainingDialogTime_ <= 30) {
- [countdownLabel_ setTextColor:[NSColor redColor]];
- }
- [self setCountdownMessage:countdownMessage];
- if (remainingDialogTime_ <= 0) {
- [messageTimer_ invalidate];
- [NSApp stopModal];
- }
-}
-
-
-
-#pragma mark Accessors
-#pragma mark -
-//=============================================================================
-
-- (NSString *)commentsValue {
- return [[commentsValue_ retain] autorelease];
-}
-
-- (void)setCommentsValue:(NSString *)value {
- if (commentsValue_ != value) {
- [commentsValue_ release];
- commentsValue_ = [value copy];
- }
-}
-
-- (NSString *)emailValue {
- return [[emailValue_ retain] autorelease];
-}
-
-- (void)setEmailValue:(NSString *)value {
- if (emailValue_ != value) {
- [emailValue_ release];
- emailValue_ = [value copy];
- }
-}
-
-- (NSString *)countdownMessage {
- return [[countdownMessage_ retain] autorelease];
-}
-
-- (void)setCountdownMessage:(NSString *)value {
- if (countdownMessage_ != value) {
- [countdownMessage_ release];
- countdownMessage_ = [value copy];
- }
-}
-
-#pragma mark -
-//=============================================================================
-- (BOOL)reportIntervalElapsed {
- float interval = [[[uploader_ parameters]
- objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue];
- NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
- NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
- NSMutableDictionary *programDict =
- [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
- NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission];
- NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
- NSTimeInterval now = CFAbsoluteTimeGetCurrent();
- NSTimeInterval spanSeconds = (now - lastTime);
-
- [programDict setObject:[NSNumber numberWithDouble:now]
- forKey:kLastSubmission];
- [ud setObject:programDict forKey:program];
- [ud synchronize];
-
- // If we've specified an interval and we're within that time, don't ask the
- // user if we should report
- GTMLoggerDebug(@"Reporter Interval: %f", interval);
- if (interval > spanSeconds) {
- GTMLoggerDebug(@"Within throttling interval, not sending report");
- return NO;
- }
- return YES;
-}
-
-- (BOOL)isOnDemand {
- return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND]
- isEqualToString:@"YES"];
-}
-
-- (BOOL)shouldSubmitSilently {
- return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM]
- isEqualToString:@"YES"];
-}
-
-- (BOOL)shouldRequestComments {
- return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS]
- isEqualToString:@"YES"];
-}
-
-- (BOOL)shouldRequestEmail {
- return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL]
- isEqualToString:@"YES"];
-}
-
-- (NSString*)shortDialogMessage {
- NSString *displayName =
- [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
- if (![displayName length])
- displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
-
- if ([self isOnDemand]) {
- // Local variable to pacify clang's -Wformat-extra-args.
- NSString* format = NSLocalizedString(@"noCrashDialogHeader", @"");
- return [NSString stringWithFormat:format, displayName];
- } else {
- // Local variable to pacify clang's -Wformat-extra-args.
- NSString* format = NSLocalizedString(@"crashDialogHeader", @"");
- return [NSString stringWithFormat:format, displayName];
- }
-}
-
-- (NSString*)explanatoryDialogText {
- NSString *displayName =
- [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
- if (![displayName length])
- displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
-
- NSString *vendor = [[uploader_ parameters] objectForKey:@BREAKPAD_VENDOR];
- if (![vendor length])
- vendor = @"unknown vendor";
-
- if ([self isOnDemand]) {
- // Local variable to pacify clang's -Wformat-extra-args.
- NSString* format = NSLocalizedString(@"noCrashDialogMsg", @"");
- return [NSString stringWithFormat:format, vendor, displayName];
- } else {
- // Local variable to pacify clang's -Wformat-extra-args.
- NSString* format = NSLocalizedString(@"crashDialogMsg", @"");
- return [NSString stringWithFormat:format, vendor];
- }
-}
-
-- (NSTimeInterval)messageTimeout {
- // Get the timeout value for the notification.
- NSTimeInterval timeout = [[[uploader_ parameters]
- objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue];
- // Require a timeout of at least a minute (except 0, which means no timeout).
- if (timeout > 0.001 && timeout < 60.0) {
- timeout = 60.0;
- }
- return timeout;
-}
-
-- (void)report {
- [uploader_ report];
-}
-
-//=============================================================================
-- (void)dealloc {
- [uploader_ release];
- [super dealloc];
-}
-
-- (void)awakeFromNib {
- [emailEntryField_ setMaximumLength:kEmailMaxLength];
- [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength];
-}
-
-@end
-
-//=============================================================================
-@implementation LengthLimitingTextField
-
-- (void)setMaximumLength:(NSUInteger)maxLength {
- maximumLength_ = maxLength;
-}
-
-// This is the method we're overriding in NSTextField, which lets us
-// limit the user's input if it makes the string too long.
-- (BOOL) textView:(NSTextView *)textView
-shouldChangeTextInRange:(NSRange)affectedCharRange
- replacementString:(NSString *)replacementString {
-
- // Sometimes the range comes in invalid, so reject if we can't
- // figure out if the replacement text is too long.
- if (affectedCharRange.location == NSNotFound) {
- return NO;
- }
- // Figure out what the new string length would be, taking into
- // account user selections.
- NSUInteger newStringLength =
- [[textView string] length] - affectedCharRange.length +
- [replacementString length];
- if (newStringLength > maximumLength_) {
- return NO;
- } else {
- return YES;
- }
-}
-
-// Cut, copy, and paste have to be caught specifically since there is no menu.
-- (BOOL)performKeyEquivalent:(NSEvent*)event {
- // Only handle the key equivalent if |self| is the text field with focus.
- NSText* fieldEditor = [self currentEditor];
- if (fieldEditor != nil) {
- // Check for a single "Command" modifier
- NSUInteger modifiers = [event modifierFlags];
- modifiers &= NSDeviceIndependentModifierFlagsMask;
- if (modifiers == NSCommandKeyMask) {
- // Now, check for Select All, Cut, Copy, or Paste key equivalents.
- NSString* characters = [event characters];
- // Select All is Command-A.
- if ([characters isEqualToString:@"a"]) {
- [fieldEditor selectAll:self];
- return YES;
- // Cut is Command-X.
- } else if ([characters isEqualToString:@"x"]) {
- [fieldEditor cut:self];
- return YES;
- // Copy is Command-C.
- } else if ([characters isEqualToString:@"c"]) {
- [fieldEditor copy:self];
- return YES;
- // Paste is Command-V.
- } else if ([characters isEqualToString:@"v"]) {
- [fieldEditor paste:self];
- return YES;
- }
- }
- }
- // Let the super class handle the rest (e.g. Command-Period will cancel).
- return [super performKeyEquivalent:event];
-}
-
-@end
-
-//=============================================================================
-int main(int argc, const char *argv[]) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-#if DEBUG
- // Log to stderr in debug builds.
- [GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]];
-#endif
- GTMLoggerDebug(@"Reporter Launched, argc=%d", argc);
- // The expectation is that there will be one argument which is the path
- // to the configuration file
- if (argc != 2) {
- exit(1);
- }
-
- Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]];
- if (!reporter) {
- GTMLoggerDebug(@"reporter initialization failed");
- exit(1);
- }
-
- // only submit a report if we have not recently crashed in the past
- BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
- BOOL okayToSend = NO;
-
- // ask user if we should send
- if (shouldSubmitReport) {
- if ([reporter shouldSubmitSilently]) {
- GTMLoggerDebug(@"Skipping confirmation and sending report");
- okayToSend = YES;
- } else {
- okayToSend = [reporter askUserPermissionToSend];
- }
- }
-
- // If we're running as root, switch over to nobody
- if (getuid() == 0 || geteuid() == 0) {
- struct passwd *pw = getpwnam("nobody");
-
- // If we can't get a non-root uid, don't send the report
- if (!pw) {
- GTMLoggerDebug(@"!pw - %s", strerror(errno));
- exit(0);
- }
-
- if (setgid(pw->pw_gid) == -1) {
- GTMLoggerDebug(@"setgid(pw->pw_gid) == -1 - %s", strerror(errno));
- exit(0);
- }
-
- if (setuid(pw->pw_uid) == -1) {
- GTMLoggerDebug(@"setuid(pw->pw_uid) == -1 - %s", strerror(errno));
- exit(0);
- }
- }
- else {
- GTMLoggerDebug(@"getuid() !=0 || geteuid() != 0");
- }
-
- if (okayToSend && shouldSubmitReport) {
- GTMLoggerDebug(@"Sending Report");
- [reporter report];
- GTMLoggerDebug(@"Report Sent!");
- } else {
- GTMLoggerDebug(@"Not sending crash report okayToSend=%d, "\
- "shouldSubmitReport=%d", okayToSend, shouldSubmitReport);
- }
-
- GTMLoggerDebug(@"Exiting with no errors");
- // Cleanup
- [reporter release];
- [pool release];
- return 0;
-}