├── .gitignore ├── .gitmodules ├── BWQuincyMain.nib ├── designable.nib └── keyedobjects.nib ├── BWQuincyManager.h ├── BWQuincyManager.m ├── Growl.framework ├── Growl ├── Headers ├── Resources └── Versions │ ├── A │ ├── Growl │ ├── Headers │ │ ├── Growl.h │ │ ├── GrowlApplicationBridge.h │ │ └── GrowlDefines.h │ ├── Resources │ │ └── Info.plist │ └── _CodeSignature │ │ └── CodeResources │ └── Current ├── HelperTool ├── AuthorizationLib │ ├── BetterAuthorizationSampleLib.c │ └── BetterAuthorizationSampleLib.h ├── BetterAuthorizationSampleLibInstallTool.c ├── TediumHelperTool.c ├── TediumHelperToolCommon.c └── TediumHelpertoolCommon.h ├── README.md ├── Resources ├── Tedium.sdef ├── awesomeclock.icns ├── dsa_pub.pem ├── gear.png ├── minus-8.png └── plus-arrow.png ├── Sparkle.framework ├── Headers ├── Resources ├── Sparkle └── Versions │ ├── A │ ├── Headers │ │ ├── SUAppcast.h │ │ ├── SUAppcastItem.h │ │ ├── SUUpdater.h │ │ ├── SUVersionComparisonProtocol.h │ │ ├── SUVersionDisplayProtocol.h │ │ └── Sparkle.h │ ├── Resources │ │ ├── Info.plist │ │ ├── License.txt │ │ ├── SUModelTranslation.plist │ │ ├── SUStatus.nib │ │ ├── cs.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── da.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── de.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── en.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── es.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── finish_installation.app │ │ │ └── Contents │ │ │ │ ├── Info.plist │ │ │ │ ├── MacOS │ │ │ │ └── finish_installation │ │ │ │ ├── PkgInfo │ │ │ │ └── Resources │ │ │ │ ├── SUStatus.nib │ │ │ │ ├── Sparkle.icns │ │ │ │ ├── cs.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── da.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── de.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── en.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── es.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── fr.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── is.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── it.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── ja.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── nl.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── pl.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── pt_BR.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── pt_PT.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── ro.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── ru.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── sl.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── sv.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── tr.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── zh_CN.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ └── zh_TW.lproj │ │ │ │ └── Sparkle.strings │ │ ├── fr.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── fr_CA.lproj │ │ ├── is.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── it.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── ja.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── ko.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ └── SUUpdatePermissionPrompt.nib │ │ ├── nl.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── pl.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── pt.lproj │ │ ├── pt_BR.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── pt_PT.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── ro.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── ru.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── sk.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ └── SUUpdatePermissionPrompt.nib │ │ ├── sl.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── sv.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── tr.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ ├── zh_CN.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ │ └── zh_TW.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ └── Sparkle.strings │ └── Sparkle │ └── Current ├── Tedium.xcodeproj └── project.pbxproj ├── Tedium ├── AppDelegate+HelperTool.h ├── AppDelegate+HelperTool.m ├── AppDelegate.h ├── AppDelegate.m ├── AppleScriptSupport.h ├── AppleScriptSupport.m ├── Destination.h ├── Destination.m ├── KeychainServices.h ├── KeychainServices.m ├── NSArray+Bonjour.h ├── NSArray+Bonjour.m ├── NSDictionary+Bonjour.h ├── NSDictionary+Bonjour.m ├── NSString+Bonjour.h ├── NSString+Bonjour.m ├── NetworkBrowser.h ├── NetworkBrowser.m ├── PopButton.h ├── PopButton.m ├── QuincyKit-Info.plist ├── QuincyKit-Prefix.pch ├── Tedium-Info.plist ├── Tedium-Prefix.pch ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ └── MainMenu.xib └── main.m └── Utilities ├── Tedium-Template.dmg ├── make_debug_image.sh ├── make_disk_image.sh ├── make_nightly_image.sh └── remove_helper_tool.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder and whatnot 2 | .DS_Store 3 | 4 | # XCode (and ancestors) per-user config (very noisy, and not relevant) 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | xcuserdata 12 | Tedium.xcodeproj/xcuserdata/dustin.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist 13 | Tedium.xcodeproj/project.xcworkspace/ 14 | 15 | 16 | # Generated files 17 | VersionX-revision.h 18 | GeneratedDocumentation 19 | 20 | 21 | # build products 22 | build/ 23 | *.[oa] 24 | 25 | # Other source repository archive directories (protects when importing) 26 | .hg 27 | .svn 28 | CVS 29 | 30 | 31 | # automatic backup files 32 | *~.nib 33 | *.swp 34 | *~ 35 | *(Autosaved).rtfd/ 36 | Backup[ ]of[ ]*.pages/ 37 | Backup[ ]of[ ]*.key/ 38 | Backup[ ]of[ ]*.numbers/ 39 | 40 | 41 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Tedium/QuincyKit"] 2 | path = Tedium/QuincyKit 3 | url = git://github.com/TheRealKerni/QuincyKit.git 4 | -------------------------------------------------------------------------------- /BWQuincyMain.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/BWQuincyMain.nib/keyedobjects.nib -------------------------------------------------------------------------------- /BWQuincyManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Andreas Linde 3 | * Kent Sutherland 4 | * 5 | * Copyright (c) 2011 Andreas Linde & Kent Sutherland. 6 | * All rights reserved. 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, 12 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following 15 | * conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | #import 31 | 32 | #define CRASHREPORTSENDER_MAX_CONSOLE_SIZE 50000 33 | 34 | typedef enum CrashAlertType { 35 | CrashAlertTypeSend = 0, 36 | CrashAlertTypeFeedback = 1, 37 | } CrashAlertType; 38 | 39 | typedef enum CrashReportStatus { 40 | // This app version is set to discontinued, no new crash reports accepted by the server 41 | CrashReportStatusFailureVersionDiscontinued = -30, 42 | 43 | // XML: Sender ersion string contains not allowed characters, only alphanumberical including space and . are allowed 44 | CrashReportStatusFailureXMLSenderVersionNotAllowed = -21, 45 | 46 | // XML: Version string contains not allowed characters, only alphanumberical including space and . are allowed 47 | CrashReportStatusFailureXMLVersionNotAllowed = -20, 48 | 49 | // SQL for adding a symoblicate todo entry in the database failed 50 | CrashReportStatusFailureSQLAddSymbolicateTodo = -18, 51 | 52 | // SQL for adding crash log in the database failed 53 | CrashReportStatusFailureSQLAddCrashlog = -17, 54 | 55 | // SQL for adding a new version in the database failed 56 | CrashReportStatusFailureSQLAddVersion = -16, 57 | 58 | // SQL for checking if the version is already added in the database failed 59 | CrashReportStatusFailureSQLCheckVersionExists = -15, 60 | 61 | // SQL for creating a new pattern for this bug and set amount of occurrances to 1 in the database failed 62 | CrashReportStatusFailureSQLAddPattern = -14, 63 | 64 | // SQL for checking the status of the bugfix version in the database failed 65 | CrashReportStatusFailureSQLCheckBugfixStatus = -13, 66 | 67 | // SQL for updating the occurances of this pattern in the database failed 68 | CrashReportStatusFailureSQLUpdatePatternOccurances = -12, 69 | 70 | // SQL for getting all the known bug patterns for the current app version in the database failed 71 | CrashReportStatusFailureSQLFindKnownPatterns = -11, 72 | 73 | // SQL for finding the bundle identifier in the database failed 74 | CrashReportStatusFailureSQLSearchAppName = -10, 75 | 76 | // the post request didn't contain valid data 77 | CrashReportStatusFailureInvalidPostData = -3, 78 | 79 | // incoming data may not be added, because e.g. bundle identifier wasn't found 80 | CrashReportStatusFailureInvalidIncomingData = -2, 81 | 82 | // database cannot be accessed, check hostname, username, password and database name settings in config.php 83 | CrashReportStatusFailureDatabaseNotAvailable = -1, 84 | 85 | CrashReportStatusUnknown = 0, 86 | 87 | CrashReportStatusAssigned = 1, 88 | 89 | CrashReportStatusSubmitted = 2, 90 | 91 | CrashReportStatusAvailable = 3, 92 | } CrashReportStatus; 93 | 94 | 95 | @class BWQuincyUI; 96 | 97 | // This protocol is used to send the image updates 98 | @protocol BWQuincyManagerDelegate 99 | 100 | @required 101 | 102 | // Invoked once the modal sheets are gone 103 | - (void) showMainApplicationWindow; 104 | 105 | @optional 106 | 107 | // Return the description the crashreport should contain, empty by default. The string will automatically be wrapped into <[DATA[ ]]>, so make sure you don't do that in your string. 108 | -(NSString *) crashReportDescription; 109 | 110 | @optional 111 | 112 | // Return the userid the crashreport should contain, empty by default 113 | -(NSString *) crashReportUserID; 114 | 115 | // Return the contact value (e.g. email) the crashreport should contain, empty by default 116 | -(NSString *) crashReportContact; 117 | @end 118 | 119 | @interface BWQuincyManager : NSObject 120 | #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 121 | 122 | #endif 123 | { 124 | CrashReportStatus _serverResult; 125 | 126 | NSInteger _statusCode; 127 | 128 | NSMutableString *_contentOfProperty; 129 | 130 | id _delegate; 131 | 132 | NSString *_submissionURL; 133 | NSString *_companyName; 134 | NSString *_appIdentifier; 135 | BOOL _autoSubmitCrashReport; 136 | 137 | NSString *_crashFile; 138 | 139 | BWQuincyUI *_quincyUI; 140 | } 141 | 142 | - (NSString*) modelVersion; 143 | 144 | + (BWQuincyManager *)sharedQuincyManager; 145 | 146 | // submission URL defines where to send the crash reports to (required) 147 | @property (nonatomic, retain) NSString *submissionURL; 148 | 149 | // defines the company name to be shown in the crash reporting dialog 150 | @property (nonatomic, retain) NSString *companyName; 151 | 152 | // delegate is required 153 | @property (nonatomic, assign) id delegate; 154 | 155 | // if YES, the crash report will be submitted without asking the user 156 | // if NO, the user will be asked if the crash report can be submitted (default) 157 | @property (nonatomic, assign, getter=isAutoSubmitCrashReport) BOOL autoSubmitCrashReport; 158 | 159 | /////////////////////////////////////////////////////////////////////////////////////////////////// 160 | // settings 161 | 162 | // If you want to use HockeyApp instead of your own server, this is required 163 | @property (nonatomic, retain) NSString *appIdentifier; 164 | 165 | 166 | - (void) cancelReport; 167 | - (void) sendReportCrash:(NSString*)crashContent 168 | description:(NSString*)description; 169 | 170 | - (NSString *) applicationName; 171 | - (NSString *) applicationVersionString; 172 | - (NSString *) applicationVersion; 173 | 174 | @end 175 | 176 | 177 | @interface BWQuincyUI : NSWindowController { 178 | IBOutlet NSTextField *descriptionTextField; 179 | IBOutlet NSTextView *crashLogTextView; 180 | 181 | IBOutlet NSTextField *noteText; 182 | 183 | IBOutlet NSButton *showButton; 184 | IBOutlet NSButton *hideButton; 185 | IBOutlet NSButton *cancelButton; 186 | IBOutlet NSButton *submitButton; 187 | 188 | BWQuincyManager *_delegate; 189 | 190 | NSString *_xml; 191 | 192 | NSString *_crashFile; 193 | NSString *_companyName; 194 | NSString *_applicationName; 195 | 196 | NSMutableString *_consoleContent; 197 | NSString *_crashLogContent; 198 | 199 | BOOL showComments; 200 | BOOL showDetails; 201 | } 202 | 203 | - (id)init:(id)delegate crashFile:(NSString *)crashFile companyName:(NSString *)companyName applicationName:(NSString *)applicationName; 204 | 205 | - (void) askCrashReportDetails; 206 | 207 | - (IBAction) cancelReport:(id)sender; 208 | - (IBAction) submitReport:(id)sender; 209 | - (IBAction) showDetails:(id)sender; 210 | - (IBAction) hideDetails:(id)sender; 211 | - (IBAction) showComments:(id)sender; 212 | 213 | - (BOOL)showComments; 214 | - (void)setShowComments:(BOOL)value; 215 | 216 | - (BOOL)showDetails; 217 | - (void)setShowDetails:(BOOL)value; 218 | 219 | @end -------------------------------------------------------------------------------- /BWQuincyManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Andreas Linde 3 | * Kent Sutherland 4 | * 5 | * Copyright (c) 2011 Andreas Linde & Kent Sutherland. 6 | * All rights reserved. 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, 12 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following 15 | * conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | #import "BWQuincyManager.h" 31 | #import 32 | 33 | @interface BWQuincyManager(private) 34 | - (void) startManager; 35 | 36 | - (void) _postXML:(NSString*)xml toURL:(NSURL*)url; 37 | - (void) searchCrashLogFile:(NSString *)path; 38 | - (BOOL) hasPendingCrashReport; 39 | - (void) returnToMainApplication; 40 | @end 41 | 42 | @interface BWQuincyUI(private) 43 | - (void) askCrashReportDetails; 44 | - (void) endCrashReporter; 45 | @end 46 | 47 | const CGFloat kCommentsHeight = 105; 48 | const CGFloat kDetailsHeight = 285; 49 | 50 | @implementation BWQuincyManager 51 | 52 | @synthesize delegate = _delegate; 53 | @synthesize submissionURL = _submissionURL; 54 | @synthesize companyName = _companyName; 55 | @synthesize appIdentifier = _appIdentifier; 56 | @synthesize autoSubmitCrashReport = _autoSubmitCrashReport; 57 | 58 | + (BWQuincyManager *)sharedQuincyManager { 59 | static BWQuincyManager *quincyManager = nil; 60 | 61 | if (quincyManager == nil) { 62 | quincyManager = [[BWQuincyManager alloc] init]; 63 | } 64 | 65 | return quincyManager; 66 | } 67 | 68 | - (id) init { 69 | if ((self = [super init])) { 70 | _serverResult = CrashReportStatusFailureDatabaseNotAvailable; 71 | _quincyUI = nil; 72 | 73 | _submissionURL = nil; 74 | _appIdentifier = nil; 75 | 76 | _crashFile = nil; 77 | 78 | self.delegate = nil; 79 | self.companyName = @""; 80 | } 81 | return self; 82 | } 83 | 84 | - (void)dealloc { 85 | _companyName = nil; 86 | _delegate = nil; 87 | _submissionURL = nil; 88 | _appIdentifier = nil; 89 | 90 | [_crashFile release]; 91 | [_quincyUI release]; 92 | 93 | [super dealloc]; 94 | } 95 | 96 | - (void) searchCrashLogFile:(NSString *)path { 97 | NSFileManager* fman = [NSFileManager defaultManager]; 98 | 99 | NSError* error; 100 | NSMutableArray* filesWithModificationDate = [NSMutableArray array]; 101 | NSArray* crashLogFiles = [fman contentsOfDirectoryAtPath:path error:&error]; 102 | NSEnumerator* filesEnumerator = [crashLogFiles objectEnumerator]; 103 | NSString* crashFile; 104 | while((crashFile = [filesEnumerator nextObject])) { 105 | NSString* crashLogPath = [path stringByAppendingPathComponent:crashFile]; 106 | NSDate* modDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:crashLogPath error:&error] fileModificationDate]; 107 | [filesWithModificationDate addObject:[NSDictionary dictionaryWithObjectsAndKeys:crashFile,@"name",crashLogPath,@"path",modDate,@"modDate",nil]]; 108 | } 109 | 110 | NSSortDescriptor* dateSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"modDate" ascending:YES] autorelease]; 111 | NSArray* sortedFiles = [filesWithModificationDate sortedArrayUsingDescriptors:[NSArray arrayWithObject:dateSortDescriptor]]; 112 | 113 | NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@", [self applicationName]]; 114 | NSArray* filteredFiles = [sortedFiles filteredArrayUsingPredicate:filterPredicate]; 115 | 116 | _crashFile = [[[filteredFiles valueForKeyPath:@"path"] lastObject] copy]; 117 | } 118 | 119 | #pragma mark - 120 | #pragma mark setter 121 | - (void)setSubmissionURL:(NSString *)anSubmissionURL { 122 | if (_submissionURL != anSubmissionURL) { 123 | [_submissionURL release]; 124 | _submissionURL = [anSubmissionURL copy]; 125 | } 126 | 127 | [self performSelector:@selector(startManager) withObject:nil afterDelay:0.1f]; 128 | } 129 | 130 | - (void)setAppIdentifier:(NSString *)anAppIdentifier { 131 | if (_appIdentifier != anAppIdentifier) { 132 | [_appIdentifier release]; 133 | _appIdentifier = [anAppIdentifier copy]; 134 | } 135 | 136 | [self setSubmissionURL:@"https://rink.hockeyapp.net/"]; 137 | } 138 | 139 | - (void)storeLastCrashDate:(NSDate *) date { 140 | [[NSUserDefaults standardUserDefaults] setValue:date forKey:@"CrashReportSender.lastCrashDate"]; 141 | [[NSUserDefaults standardUserDefaults] synchronize]; 142 | } 143 | 144 | - (NSDate *)loadLastCrashDate { 145 | NSDate *date = [[NSUserDefaults standardUserDefaults] valueForKey:@"CrashReportSender.lastCrashDate"]; 146 | return date ?: [NSDate distantPast]; 147 | } 148 | 149 | - (void)storeAppVersion:(NSString *) version { 150 | [[NSUserDefaults standardUserDefaults] setValue:version forKey:@"CrashReportSender.appVersion"]; 151 | [[NSUserDefaults standardUserDefaults] synchronize]; 152 | } 153 | 154 | - (NSString *)loadAppVersion { 155 | NSString *appVersion = [[NSUserDefaults standardUserDefaults] valueForKey:@"CrashReportSender.appVersion"]; 156 | return appVersion ?: nil; 157 | } 158 | 159 | #pragma mark - 160 | #pragma mark GetCrashData 161 | 162 | - (BOOL) hasPendingCrashReport { 163 | BOOL returnValue = NO; 164 | 165 | NSString *appVersion = [self loadAppVersion]; 166 | NSDate *lastCrashDate = [self loadLastCrashDate]; 167 | 168 | if (!appVersion || ![appVersion isEqualToString:[self applicationVersion]] || [lastCrashDate isEqualToDate:[NSDate distantPast]]) { 169 | [self storeAppVersion:[self applicationVersion]]; 170 | [self storeLastCrashDate:[NSDate date]]; 171 | return NO; 172 | } 173 | 174 | NSArray* libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, TRUE); 175 | // Snow Leopard is having the log files in another location 176 | [self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/DiagnosticReports"]]; 177 | if (_crashFile == nil) { 178 | [self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/CrashReporter"]]; 179 | if (_crashFile == nil) { 180 | NSString *sandboxFolder = [NSString stringWithFormat:@"/Containers/%@/Data/Library", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]]; 181 | if ([[libraryDirectories lastObject] rangeOfString:sandboxFolder].location != NSNotFound) { 182 | NSString *libFolderName = [[libraryDirectories lastObject] stringByReplacingOccurrencesOfString:sandboxFolder withString:@""]; 183 | [self searchCrashLogFile:[libFolderName stringByAppendingPathComponent:@"Logs/DiagnosticReports"]]; 184 | } 185 | } 186 | } 187 | 188 | if (_crashFile) { 189 | NSError* error; 190 | 191 | NSDate *crashLogModificationDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:_crashFile error:&error] fileModificationDate]; 192 | unsigned long long crashLogFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:_crashFile error:&error] fileSize]; 193 | if ([crashLogModificationDate compare: lastCrashDate] == NSOrderedDescending && crashLogFileSize > 0) { 194 | [self storeLastCrashDate:crashLogModificationDate]; 195 | returnValue = YES; 196 | } 197 | } 198 | 199 | return returnValue; 200 | } 201 | 202 | - (void) returnToMainApplication { 203 | if ( self.delegate != nil && [self.delegate respondsToSelector:@selector(showMainApplicationWindow)]) 204 | [self.delegate showMainApplicationWindow]; 205 | } 206 | 207 | - (void) startManager { 208 | if ([self hasPendingCrashReport]) { 209 | if (!self.autoSubmitCrashReport) { 210 | _quincyUI = [[BWQuincyUI alloc] init:self crashFile:_crashFile companyName:_companyName applicationName:[self applicationName]]; 211 | [_quincyUI askCrashReportDetails]; 212 | } else { 213 | NSError* error = nil; 214 | NSString *crashLogs = [NSString stringWithContentsOfFile:_crashFile encoding:NSUTF8StringEncoding error:&error]; 215 | if (!error) { 216 | NSString *lastCrash = [[crashLogs componentsSeparatedByString: @"**********\n\n"] lastObject]; 217 | 218 | NSString* description = @""; 219 | 220 | if (_delegate && [_delegate respondsToSelector:@selector(crashReportDescription)]) { 221 | description = [_delegate crashReportDescription]; 222 | } 223 | 224 | [self sendReportCrash:lastCrash description:description]; 225 | } 226 | } 227 | } else { 228 | [self returnToMainApplication]; 229 | } 230 | } 231 | 232 | - (NSString*) modelVersion { 233 | NSString * modelString = nil; 234 | int modelInfo[2] = { CTL_HW, HW_MODEL }; 235 | size_t modelSize; 236 | 237 | if (sysctl(modelInfo, 238 | 2, 239 | NULL, 240 | &modelSize, 241 | NULL, 0) == 0) { 242 | void * modelData = malloc(modelSize); 243 | 244 | if (modelData) { 245 | if (sysctl(modelInfo, 246 | 2, 247 | modelData, 248 | &modelSize, 249 | NULL, 0) == 0) { 250 | modelString = [NSString stringWithUTF8String:modelData]; 251 | } 252 | 253 | free(modelData); 254 | } 255 | } 256 | 257 | return modelString; 258 | } 259 | 260 | 261 | 262 | - (void) cancelReport { 263 | [self returnToMainApplication]; 264 | } 265 | 266 | 267 | - (void) sendReportCrash:(NSString*)crashContent 268 | description:(NSString*)notes 269 | { 270 | NSString *userid = @""; 271 | NSString *contact = @""; 272 | 273 | SInt32 versionMajor, versionMinor, versionBugFix; 274 | if (Gestalt(gestaltSystemVersionMajor, &versionMajor) != noErr) versionMajor = 0; 275 | if (Gestalt(gestaltSystemVersionMinor, &versionMinor) != noErr) versionMinor= 0; 276 | if (Gestalt(gestaltSystemVersionBugFix, &versionBugFix) != noErr) versionBugFix = 0; 277 | 278 | NSString* xml = [NSString stringWithFormat:@"%s%s%@%@%@%@%@%@", 279 | [[self applicationName] UTF8String], 280 | [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"] UTF8String], 281 | [NSString stringWithFormat:@"%i.%i.%i", versionMajor, versionMinor, versionBugFix], 282 | [self applicationVersion], 283 | [self applicationVersion], 284 | [self modelVersion], 285 | userid, 286 | contact, 287 | notes, 288 | crashContent 289 | ]; 290 | 291 | 292 | [self returnToMainApplication]; 293 | 294 | [self _postXML:[NSString stringWithFormat:@"%@", xml] toURL:[NSURL URLWithString:self.submissionURL]]; 295 | } 296 | 297 | - (void)_postXML:(NSString*)xml toURL:(NSURL*)url { 298 | NSMutableURLRequest *request = nil; 299 | NSString *boundary = @"----FOO"; 300 | 301 | if (self.appIdentifier) { 302 | request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes", 303 | self.submissionURL, 304 | [self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] 305 | ] 306 | ]]; 307 | } else { 308 | request = [NSMutableURLRequest requestWithURL:url]; 309 | } 310 | 311 | [request setValue:@"Quincy/Mac" forHTTPHeaderField:@"User-Agent"]; 312 | [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; 313 | [request setTimeoutInterval: 15]; 314 | [request setHTTPMethod:@"POST"]; 315 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; 316 | [request setValue:contentType forHTTPHeaderField:@"Content-type"]; 317 | 318 | NSMutableData *postBody = [NSMutableData data]; 319 | [postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 320 | if (self.appIdentifier) { 321 | [postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 322 | [postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 323 | } else { 324 | [postBody appendData:[@"Content-Disposition: form-data; name=\"xmlstring\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 325 | } 326 | [postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]]; 327 | [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 328 | [request setHTTPBody:postBody]; 329 | 330 | _serverResult = CrashReportStatusUnknown; 331 | _statusCode = 200; 332 | 333 | NSHTTPURLResponse *response = nil; 334 | NSError *error = nil; 335 | 336 | NSData *responseData = nil; 337 | responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 338 | _statusCode = [response statusCode]; 339 | 340 | if (responseData != nil) { 341 | if (_statusCode >= 200 && _statusCode < 400) { 342 | NSXMLParser *parser = [[NSXMLParser alloc] initWithData:responseData]; 343 | // Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks. 344 | [parser setDelegate:self]; 345 | // Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser. 346 | [parser setShouldProcessNamespaces:NO]; 347 | [parser setShouldReportNamespacePrefixes:NO]; 348 | [parser setShouldResolveExternalEntities:NO]; 349 | 350 | [parser parse]; 351 | 352 | [parser release]; 353 | } 354 | } 355 | } 356 | 357 | 358 | #pragma mark NSXMLParser 359 | 360 | - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { 361 | if (qName) { 362 | elementName = qName; 363 | } 364 | 365 | if ([elementName isEqualToString:@"result"]) { 366 | _contentOfProperty = [NSMutableString string]; 367 | } 368 | } 369 | 370 | - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { 371 | if (qName) { 372 | elementName = qName; 373 | } 374 | 375 | if ([elementName isEqualToString:@"result"]) { 376 | if ([_contentOfProperty intValue] > _serverResult) { 377 | _serverResult = [_contentOfProperty intValue]; 378 | } 379 | } 380 | } 381 | 382 | 383 | - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 384 | if (_contentOfProperty) { 385 | // If the current element is one whose content we care about, append 'string' 386 | // to the property that holds the content of the current element. 387 | if (string != nil) { 388 | [_contentOfProperty appendString:string]; 389 | } 390 | } 391 | } 392 | 393 | 394 | #pragma mark GetterSetter 395 | 396 | - (NSString *) applicationName { 397 | NSString *applicationName = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleExecutable"]; 398 | 399 | if (!applicationName) 400 | applicationName = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleExecutable"]; 401 | 402 | return applicationName; 403 | } 404 | 405 | 406 | - (NSString*) applicationVersionString { 407 | NSString* string = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleShortVersionString"]; 408 | 409 | if (!string) 410 | string = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleShortVersionString"]; 411 | 412 | return string; 413 | } 414 | 415 | - (NSString *) applicationVersion { 416 | NSString* string = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleVersion"]; 417 | 418 | if (!string) 419 | string = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleVersion"]; 420 | 421 | return string; 422 | } 423 | 424 | @end 425 | 426 | 427 | 428 | 429 | @implementation BWQuincyUI 430 | 431 | - (id)init:(id)delegate crashFile:(NSString *)crashFile companyName:(NSString *)companyName applicationName:(NSString *)applicationName { 432 | 433 | self = [super initWithWindowNibName: @"BWQuincyMain"]; 434 | 435 | if ( self != nil) { 436 | _xml = nil; 437 | _delegate = delegate; 438 | _crashFile = crashFile; 439 | _companyName = companyName; 440 | _applicationName = applicationName; 441 | [self setShowComments: YES]; 442 | [self setShowDetails: NO]; 443 | 444 | NSRect windowFrame = [[self window] frame]; 445 | windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kDetailsHeight); 446 | windowFrame.origin.y -= kDetailsHeight; 447 | [[self window] setFrame: windowFrame 448 | display: YES 449 | animate: NO]; 450 | 451 | } 452 | return self; 453 | } 454 | 455 | 456 | - (void) endCrashReporter { 457 | [self close]; 458 | } 459 | 460 | 461 | - (IBAction) showComments: (id) sender { 462 | NSRect windowFrame = [[self window] frame]; 463 | 464 | if ([sender intValue]) { 465 | [self setShowComments: NO]; 466 | 467 | windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height + kCommentsHeight); 468 | windowFrame.origin.y -= kCommentsHeight; 469 | [[self window] setFrame: windowFrame 470 | display: YES 471 | animate: YES]; 472 | 473 | [self setShowComments: YES]; 474 | } else { 475 | [self setShowComments: NO]; 476 | 477 | windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kCommentsHeight); 478 | windowFrame.origin.y += kCommentsHeight; 479 | [[self window] setFrame: windowFrame 480 | display: YES 481 | animate: YES]; 482 | } 483 | } 484 | 485 | 486 | - (IBAction) showDetails:(id)sender { 487 | NSRect windowFrame = [[self window] frame]; 488 | 489 | windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height + kDetailsHeight); 490 | windowFrame.origin.y -= kDetailsHeight; 491 | [[self window] setFrame: windowFrame 492 | display: YES 493 | animate: YES]; 494 | 495 | [self setShowDetails:YES]; 496 | 497 | } 498 | 499 | 500 | - (IBAction) hideDetails:(id)sender { 501 | NSRect windowFrame = [[self window] frame]; 502 | 503 | [self setShowDetails:NO]; 504 | 505 | windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kDetailsHeight); 506 | windowFrame.origin.y += kDetailsHeight; 507 | [[self window] setFrame: windowFrame 508 | display: YES 509 | animate: YES]; 510 | } 511 | 512 | 513 | - (IBAction) cancelReport:(id)sender { 514 | [self endCrashReporter]; 515 | [NSApp stopModal]; 516 | 517 | if ( _delegate != nil && [_delegate respondsToSelector:@selector(cancelReport)]) 518 | [_delegate cancelReport]; 519 | } 520 | 521 | - (void) _sendReportAfterDelay { 522 | if ( _delegate != nil && [_delegate respondsToSelector:@selector(sendReport:)]) { 523 | NSString *notes = [NSString stringWithFormat:@"Comments:\n%@\n\nConsole:\n%@", [descriptionTextField stringValue], _consoleContent]; 524 | 525 | [_delegate sendReportCrash:_crashLogContent description:notes]; 526 | } 527 | } 528 | 529 | - (IBAction) submitReport:(id)sender { 530 | [submitButton setEnabled:NO]; 531 | 532 | [[self window] makeFirstResponder: nil]; 533 | 534 | [self performSelector:@selector(_sendReportAfterDelay) withObject:nil afterDelay:0.01]; 535 | 536 | [self endCrashReporter]; 537 | [NSApp stopModal]; 538 | } 539 | 540 | 541 | - (void) askCrashReportDetails { 542 | NSError *error; 543 | 544 | [[self window] setTitle:[NSString stringWithFormat:NSLocalizedString(@"Problem Report for %@", @"Window title"), _applicationName]]; 545 | 546 | [[descriptionTextField cell] setPlaceholderString:NSLocalizedString(@"Please describe any steps needed to trigger the problem", @"User description placeholder")]; 547 | [noteText setStringValue:NSLocalizedString(@"No personal information will be sent with this report.", @"Note text")]; 548 | 549 | // get the crash log 550 | NSString *crashLogs = [NSString stringWithContentsOfFile:_crashFile encoding:NSUTF8StringEncoding error:&error]; 551 | NSString *lastCrash = [[crashLogs componentsSeparatedByString: @"**********\n\n"] lastObject]; 552 | 553 | _crashLogContent = lastCrash; 554 | 555 | // get the console log 556 | NSEnumerator *theEnum = [[[NSString stringWithContentsOfFile:@"/private/var/log/system.log" encoding:NSUTF8StringEncoding error:&error] componentsSeparatedByString: @"\n"] objectEnumerator]; 557 | NSString* currentObject; 558 | NSMutableArray* applicationStrings = [NSMutableArray array]; 559 | 560 | NSString* searchString = [[_delegate applicationName] stringByAppendingString:@"["]; 561 | while ( (currentObject = [theEnum nextObject]) ) { 562 | if ([currentObject rangeOfString:searchString].location != NSNotFound) 563 | [applicationStrings addObject: currentObject]; 564 | } 565 | 566 | _consoleContent = [NSMutableString string]; 567 | 568 | NSInteger i; 569 | for(i = ((NSInteger)[applicationStrings count])-1; (i>=0 && i>((NSInteger)[applicationStrings count])-100); i--) { 570 | [_consoleContent appendString:[applicationStrings objectAtIndex:i]]; 571 | [_consoleContent appendString:@"\n"]; 572 | } 573 | 574 | // Now limit the content to CRASHREPORTSENDER_MAX_CONSOLE_SIZE (default: 50kByte) 575 | if ([_consoleContent length] > CRASHREPORTSENDER_MAX_CONSOLE_SIZE) { 576 | _consoleContent = (NSMutableString *)[_consoleContent substringWithRange:NSMakeRange([_consoleContent length]-CRASHREPORTSENDER_MAX_CONSOLE_SIZE-1, CRASHREPORTSENDER_MAX_CONSOLE_SIZE)]; 577 | } 578 | 579 | [crashLogTextView setString:[NSString stringWithFormat:@"%@\n\n%@", _crashLogContent, _consoleContent]]; 580 | 581 | 582 | NSBeep(); 583 | [NSApp runModalForWindow:[self window]]; 584 | } 585 | 586 | 587 | - (void)dealloc { 588 | _companyName = nil; 589 | _delegate = nil; 590 | 591 | [super dealloc]; 592 | } 593 | 594 | 595 | - (BOOL)showComments { 596 | return showComments; 597 | } 598 | 599 | 600 | - (void)setShowComments:(BOOL)value { 601 | showComments = value; 602 | } 603 | 604 | 605 | - (BOOL)showDetails { 606 | return showDetails; 607 | } 608 | 609 | 610 | - (void)setShowDetails:(BOOL)value { 611 | showDetails = value; 612 | } 613 | 614 | #pragma mark NSTextField Delegate 615 | 616 | - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector { 617 | BOOL commandHandled = NO; 618 | 619 | if (commandSelector == @selector(insertNewline:)) { 620 | [textView insertNewlineIgnoringFieldEditor:self]; 621 | commandHandled = YES; 622 | } 623 | 624 | return commandHandled; 625 | } 626 | 627 | @end 628 | 629 | -------------------------------------------------------------------------------- /Growl.framework/Growl: -------------------------------------------------------------------------------- 1 | Versions/Current/Growl -------------------------------------------------------------------------------- /Growl.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Growl.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Growl.framework/Versions/A/Growl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Growl.framework/Versions/A/Growl -------------------------------------------------------------------------------- /Growl.framework/Versions/A/Headers/Growl.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __OBJC__ 4 | # include 5 | #endif 6 | -------------------------------------------------------------------------------- /Growl.framework/Versions/A/Headers/GrowlDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // GrowlDefines.h 3 | // 4 | 5 | #ifndef _GROWLDEFINES_H 6 | #define _GROWLDEFINES_H 7 | 8 | #ifdef __OBJC__ 9 | #define XSTR(x) (@x) 10 | #else 11 | #define XSTR CFSTR 12 | #endif 13 | 14 | /*! @header GrowlDefines.h 15 | * @abstract Defines all the notification keys. 16 | * @discussion Defines all the keys used for registration with Growl and for 17 | * Growl notifications. 18 | * 19 | * Most applications should use the functions or methods of Growl.framework 20 | * instead of posting notifications such as those described here. 21 | * @updated 2004-01-25 22 | */ 23 | 24 | // UserInfo Keys for Registration 25 | #pragma mark UserInfo Keys for Registration 26 | 27 | /*! @group Registration userInfo keys */ 28 | /* @abstract Keys for the userInfo dictionary of a GROWL_APP_REGISTRATION distributed notification. 29 | * @discussion The values of these keys describe the application and the 30 | * notifications it may post. 31 | * 32 | * Your application must register with Growl before it can post Growl 33 | * notifications (and have them not be ignored). However, as of Growl 0.6, 34 | * posting GROWL_APP_REGISTRATION notifications directly is no longer the 35 | * preferred way to register your application. Your application should instead 36 | * use Growl.framework's delegate system. 37 | * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for 38 | * more information. 39 | */ 40 | 41 | /*! @defined GROWL_APP_NAME 42 | * @abstract The name of your application. 43 | * @discussion The name of your application. This should remain stable between 44 | * different versions and incarnations of your application. 45 | * For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and 46 | * "SurfWriter Lite" are not. 47 | */ 48 | #define GROWL_APP_NAME XSTR("ApplicationName") 49 | /*! @defined GROWL_APP_ID 50 | * @abstract The bundle identifier of your application. 51 | * @discussion The bundle identifier of your application. This key should 52 | * be unique for your application while there may be several applications 53 | * with the same GROWL_APP_NAME. 54 | * This key is optional. 55 | */ 56 | #define GROWL_APP_ID XSTR("ApplicationId") 57 | /*! @defined GROWL_APP_ICON_DATA 58 | * @abstract The image data for your application's icon. 59 | * @discussion Image data representing your application's icon. This may be 60 | * superimposed on a notification icon as a badge, used as the notification 61 | * icon when a notification-specific icon is not supplied, or ignored 62 | * altogether, depending on the display. Must be in a format supported by 63 | * NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. 64 | * 65 | * Optional. Not supported by all display plugins. 66 | */ 67 | #define GROWL_APP_ICON_DATA XSTR("ApplicationIcon") 68 | /*! @defined GROWL_NOTIFICATIONS_DEFAULT 69 | * @abstract The array of notifications to turn on by default. 70 | * @discussion These are the names of the notifications that should be enabled 71 | * by default when your application registers for the first time. If your 72 | * application reregisters, Growl will look here for any new notification 73 | * names found in GROWL_NOTIFICATIONS_ALL, but ignore any others. 74 | */ 75 | #define GROWL_NOTIFICATIONS_DEFAULT XSTR("DefaultNotifications") 76 | /*! @defined GROWL_NOTIFICATIONS_ALL 77 | * @abstract The array of all notifications your application can send. 78 | * @discussion These are the names of all of the notifications that your 79 | * application may post. See GROWL_NOTIFICATION_NAME for a discussion of good 80 | * notification names. 81 | */ 82 | #define GROWL_NOTIFICATIONS_ALL XSTR("AllNotifications") 83 | /*! @defined GROWL_NOTIFICATIONS_HUMAN_READABLE_DESCRIPTIONS 84 | * @abstract A dictionary of human-readable names for your notifications. 85 | * @discussion By default, the Growl UI will display notifications by the names given in GROWL_NOTIFICATIONS_ALL 86 | * which correspond to the GROWL_NOTIFICATION_NAME. This dictionary specifies the human-readable name to display. 87 | * The keys of the dictionary are GROWL_NOTIFICATION_NAME strings; the objects are the human-readable versions. 88 | * For any GROWL_NOTIFICATION_NAME not specific in this dictionary, the GROWL_NOTIFICATION_NAME will be displayed. 89 | * 90 | * This key is optional. 91 | */ 92 | #define GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES XSTR("HumanReadableNames") 93 | /*! @defined GROWL_NOTIFICATIONS_DESCRIPTIONS 94 | * @abstract A dictionary of descriptions of _when_ each notification occurs 95 | * @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are 96 | * descriptions of _when_ each notification occurs, such as "You received a new mail message" or 97 | * "A file finished downloading". 98 | * 99 | * This key is optional. 100 | */ 101 | #define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions") 102 | 103 | /*! @defined GROWL_TICKET_VERSION 104 | * @abstract The version of your registration ticket. 105 | * @discussion Include this key in a ticket plist file that you put in your 106 | * application bundle for auto-discovery. The current ticket version is 1. 107 | */ 108 | #define GROWL_TICKET_VERSION XSTR("TicketVersion") 109 | // UserInfo Keys for Notifications 110 | #pragma mark UserInfo Keys for Notifications 111 | 112 | /*! @group Notification userInfo keys */ 113 | /* @abstract Keys for the userInfo dictionary of a GROWL_NOTIFICATION distributed notification. 114 | * @discussion The values of these keys describe the content of a Growl 115 | * notification. 116 | * 117 | * Not all of these keys are supported by all displays. Only the name, title, 118 | * and description of a notification are universal. Most of the built-in 119 | * displays do support all of these keys, and most other visual displays 120 | * probably will also. But, as of 0.6, the Log, MailMe, and Speech displays 121 | * support only textual data. 122 | */ 123 | 124 | /*! @defined GROWL_NOTIFICATION_NAME 125 | * @abstract The name of the notification. 126 | * @discussion The name of the notification. Note that if you do not define 127 | * GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES when registering your ticket originally this name 128 | * will the one displayed within the Growl preference pane and should be human-readable. 129 | */ 130 | #define GROWL_NOTIFICATION_NAME XSTR("NotificationName") 131 | /*! @defined GROWL_NOTIFICATION_TITLE 132 | * @abstract The title to display in the notification. 133 | * @discussion The title of the notification. Should be very brief. 134 | * The title usually says what happened, e.g. "Download complete". 135 | */ 136 | #define GROWL_NOTIFICATION_TITLE XSTR("NotificationTitle") 137 | /*! @defined GROWL_NOTIFICATION_DESCRIPTION 138 | * @abstract The description to display in the notification. 139 | * @discussion The description should be longer and more verbose than the title. 140 | * The description usually tells the subject of the action, 141 | * e.g. "Growl-0.6.dmg downloaded in 5.02 minutes". 142 | */ 143 | #define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription") 144 | /*! @defined GROWL_NOTIFICATION_ICON 145 | * @discussion Image data for the notification icon. Image data must be in a format 146 | * supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. 147 | * 148 | * Optional. Not supported by all display plugins. 149 | */ 150 | #define GROWL_NOTIFICATION_ICON_DATA XSTR("NotificationIcon") 151 | /*! @defined GROWL_NOTIFICATION_APP_ICON 152 | * @discussion Image data for the application icon, in case GROWL_APP_ICON does 153 | * not apply for some reason. Image data be in a format supported by NSImage, such 154 | * as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. 155 | * 156 | * Optional. Not supported by all display plugins. 157 | */ 158 | #define GROWL_NOTIFICATION_APP_ICON_DATA XSTR("NotificationAppIcon") 159 | /*! @defined GROWL_NOTIFICATION_PRIORITY 160 | * @discussion The priority of the notification as an integer number from 161 | * -2 to +2 (+2 being highest). 162 | * 163 | * Optional. Not supported by all display plugins. 164 | */ 165 | #define GROWL_NOTIFICATION_PRIORITY XSTR("NotificationPriority") 166 | /*! @defined GROWL_NOTIFICATION_STICKY 167 | * @discussion A Boolean number controlling whether the notification is sticky. 168 | * 169 | * Optional. Not supported by all display plugins. 170 | */ 171 | #define GROWL_NOTIFICATION_STICKY XSTR("NotificationSticky") 172 | /*! @defined GROWL_NOTIFICATION_CLICK_CONTEXT 173 | * @abstract Identifies which notification was clicked. 174 | * @discussion An identifier for the notification for clicking purposes. 175 | * 176 | * This will be passed back to the application when the notification is 177 | * clicked. It must be plist-encodable (a data, dictionary, array, number, or 178 | * string object), and it should be unique for each notification you post. 179 | * A good click context would be a UUID string returned by NSProcessInfo or 180 | * CFUUID. 181 | * 182 | * Optional. Not supported by all display plugins. 183 | */ 184 | #define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext") 185 | 186 | /*! @defined GROWL_NOTIFICATION_IDENTIFIER 187 | * @abstract An identifier for the notification for coalescing purposes. 188 | * Notifications with the same identifier fall into the same class; only 189 | * the last notification of a class is displayed on the screen. If a 190 | * notification of the same class is currently being displayed, it is 191 | * replaced by this notification. 192 | * 193 | * Optional. Not supported by all display plugins. 194 | */ 195 | #define GROWL_NOTIFICATION_IDENTIFIER XSTR("GrowlNotificationIdentifier") 196 | 197 | /*! @defined GROWL_APP_PID 198 | * @abstract The process identifier of the process which sends this 199 | * notification. If this field is set, the application will only receive 200 | * clicked and timed out notifications which originate from this process. 201 | * 202 | * Optional. 203 | */ 204 | #define GROWL_APP_PID XSTR("ApplicationPID") 205 | 206 | /*! @defined GROWL_NOTIFICATION_PROGRESS 207 | * @abstract If this key is set, it should contain a double value wrapped 208 | * in a NSNumber which describes some sort of progress (from 0.0 to 100.0). 209 | * If this is key is not set, no progress bar is shown. 210 | * 211 | * Optional. Not supported by all display plugins. 212 | */ 213 | #define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress") 214 | 215 | // Notifications 216 | #pragma mark Notifications 217 | 218 | /*! @group Notification names */ 219 | /* @abstract Names of distributed notifications used by Growl. 220 | * @discussion These are notifications used by applications (directly or 221 | * indirectly) to interact with Growl, and by Growl for interaction between 222 | * its components. 223 | * 224 | * Most of these should no longer be used in Growl 0.6 and later, in favor of 225 | * Growl.framework's GrowlApplicationBridge APIs. 226 | */ 227 | 228 | /*! @defined GROWL_APP_REGISTRATION 229 | * @abstract The distributed notification for registering your application. 230 | * @discussion This is the name of the distributed notification that can be 231 | * used to register applications with Growl. 232 | * 233 | * The userInfo dictionary for this notification can contain these keys: 234 | *
    235 | *
  • GROWL_APP_NAME
  • 236 | *
  • GROWL_APP_ICON_DATA
  • 237 | *
  • GROWL_NOTIFICATIONS_ALL
  • 238 | *
  • GROWL_NOTIFICATIONS_DEFAULT
  • 239 | *
240 | * 241 | * No longer recommended as of Growl 0.6. An alternate method of registering 242 | * is to use Growl.framework's delegate system. 243 | * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for 244 | * more information. 245 | */ 246 | #define GROWL_APP_REGISTRATION XSTR("GrowlApplicationRegistrationNotification") 247 | /*! @defined GROWL_APP_REGISTRATION_CONF 248 | * @abstract The distributed notification for confirming registration. 249 | * @discussion The name of the distributed notification sent to confirm the 250 | * registration. Used by the Growl preference pane. Your application probably 251 | * does not need to use this notification. 252 | */ 253 | #define GROWL_APP_REGISTRATION_CONF XSTR("GrowlApplicationRegistrationConfirmationNotification") 254 | /*! @defined GROWL_NOTIFICATION 255 | * @abstract The distributed notification for Growl notifications. 256 | * @discussion This is what it all comes down to. This is the name of the 257 | * distributed notification that your application posts to actually send a 258 | * Growl notification. 259 | * 260 | * The userInfo dictionary for this notification can contain these keys: 261 | *
    262 | *
  • GROWL_NOTIFICATION_NAME (required)
  • 263 | *
  • GROWL_NOTIFICATION_TITLE (required)
  • 264 | *
  • GROWL_NOTIFICATION_DESCRIPTION (required)
  • 265 | *
  • GROWL_NOTIFICATION_ICON
  • 266 | *
  • GROWL_NOTIFICATION_APP_ICON
  • 267 | *
  • GROWL_NOTIFICATION_PRIORITY
  • 268 | *
  • GROWL_NOTIFICATION_STICKY
  • 269 | *
  • GROWL_NOTIFICATION_CLICK_CONTEXT
  • 270 | *
  • GROWL_APP_NAME (required)
  • 271 | *
272 | * 273 | * No longer recommended as of Growl 0.6. Three alternate methods of posting 274 | * notifications are +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:], 275 | * Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext, and 276 | * Growl_PostNotification. 277 | */ 278 | #define GROWL_NOTIFICATION XSTR("GrowlNotification") 279 | /*! @defined GROWL_PING 280 | * @abstract A distributed notification to check whether Growl is running. 281 | * @discussion This is used by the Growl preference pane. If it receives a 282 | * GROWL_PONG, the preference pane takes this to mean that Growl is running. 283 | */ 284 | #define GROWL_PING XSTR("Honey, Mind Taking Out The Trash") 285 | /*! @defined GROWL_PONG 286 | * @abstract The distributed notification sent in reply to GROWL_PING. 287 | * @discussion GrowlHelperApp posts this in reply to GROWL_PING. 288 | */ 289 | #define GROWL_PONG XSTR("What Do You Want From Me, Woman") 290 | /*! @defined GROWL_IS_READY 291 | * @abstract The distributed notification sent when Growl starts up. 292 | * @discussion GrowlHelperApp posts this when it has begin listening on all of 293 | * its sources for new notifications. GrowlApplicationBridge (in 294 | * Growl.framework), upon receiving this notification, reregisters using the 295 | * registration dictionary supplied by its delegate. 296 | */ 297 | #define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!") 298 | 299 | 300 | /*! @defined GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX 301 | * @abstract Part of the name of the distributed notification sent when a supported notification is clicked. 302 | * @discussion When a Growl notification with a click context is clicked on by 303 | * the user, Growl posts a distributed notification whose name is in the format: 304 | * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX] 305 | * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. 306 | */ 307 | #define GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX XSTR("GrowlClicked!") 308 | 309 | /*! @defined GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX 310 | * @abstract Part of the name of the distributed notification sent when a supported notification times out without being clicked. 311 | * @discussion When a Growl notification with a click context times out, Growl posts a distributed notification 312 | * whose name is in the format: 313 | * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX] 314 | * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. 315 | * NOTE: The user may have actually clicked the 'close' button; this triggers an *immediate* time-out of the notification. 316 | */ 317 | #define GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX XSTR("GrowlTimedOut!") 318 | 319 | /*! @group Other symbols */ 320 | /* Symbols which don't fit into any of the other categories. */ 321 | 322 | /*! @defined GROWL_KEY_CLICKED_CONTEXT 323 | * @abstract Used internally as the key for the clickedContext passed over DNC. 324 | * @discussion This key is used in GROWL_NOTIFICATION_CLICKED, and contains the 325 | * click context that was supplied in the original notification. 326 | */ 327 | #define GROWL_KEY_CLICKED_CONTEXT XSTR("ClickedContext") 328 | /*! @defined GROWL_REG_DICT_EXTENSION 329 | * @abstract The filename extension for registration dictionaries. 330 | * @discussion The GrowlApplicationBridge in Growl.framework registers with 331 | * Growl by creating a file with the extension of .(GROWL_REG_DICT_EXTENSION) 332 | * and opening it in the GrowlHelperApp. This happens whether or not Growl is 333 | * running; if it was stopped, it quits immediately without listening for 334 | * notifications. 335 | */ 336 | #define GROWL_REG_DICT_EXTENSION XSTR("growlRegDict") 337 | 338 | 339 | #define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition" 340 | 341 | #endif //ndef _GROWLDEFINES_H 342 | -------------------------------------------------------------------------------- /Growl.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 11C74 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | Growl 11 | CFBundleIdentifier 12 | com.growl.growlframework 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.3.1 19 | CFBundleSignature 20 | GRRR 21 | CFBundleVersion 22 | 1.3.1 23 | DTCompiler 24 | com.apple.compilers.llvm.clang.1_0 25 | DTPlatformBuild 26 | 4D199 27 | DTPlatformVersion 28 | GM 29 | DTSDKBuild 30 | 11C63 31 | DTSDKName 32 | macosx10.7 33 | DTXcode 34 | 0420 35 | DTXcodeBuild 36 | 4D199 37 | NSPrincipalClass 38 | GrowlApplicationBridge 39 | 40 | 41 | -------------------------------------------------------------------------------- /Growl.framework/Versions/A/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Resources/Info.plist 8 | 9 | SwzGt9RQsuVafBBrfBalB75dCwU= 10 | 11 | 12 | rules 13 | 14 | ^Resources/ 15 | 16 | ^Resources/.*\.lproj/ 17 | 18 | optional 19 | 20 | weight 21 | 1000 22 | 23 | ^Resources/.*\.lproj/locversion.plist$ 24 | 25 | omit 26 | 27 | weight 28 | 1100 29 | 30 | ^version.plist$ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Growl.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /HelperTool/BetterAuthorizationSampleLibInstallTool.c: -------------------------------------------------------------------------------- 1 | /* 2 | File: BetterAuthorizationSampleLibInstallTool.c 3 | 4 | Contains: Tool to install BetterAuthorizationSampleLib-based privileged helper tools. 5 | 6 | Written by: DTS 7 | 8 | Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved. 9 | 10 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple, Inc. 11 | ("Apple") in consideration of your agreement to the following terms, and your 12 | use, installation, modification or redistribution of this Apple software 13 | constitutes acceptance of these terms. If you do not agree with these terms, 14 | please do not use, install, modify or redistribute this Apple software. 15 | 16 | In consideration of your agreement to abide by the following terms, and subject 17 | to these terms, Apple grants you a personal, non-exclusive license, under Apple's 18 | copyrights in this original Apple software (the "Apple Software"), to use, 19 | reproduce, modify and redistribute the Apple Software, with or without 20 | modifications, in source and/or binary forms; provided that if you redistribute 21 | the Apple Software in its entirety and without modifications, you must retain 22 | this notice and the following text and disclaimers in all such redistributions of 23 | the Apple Software. Neither the name, trademarks, service marks or logos of 24 | Apple, Inc. may be used to endorse or promote products derived from the 25 | Apple Software without specific prior written permission from Apple. Except as 26 | expressly stated in this notice, no other rights or licenses, express or implied, 27 | are granted by Apple herein, including but not limited to any patent rights that 28 | may be infringed by your derivative works or by other works in which the Apple 29 | Software may be incorporated. 30 | 31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO 32 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 33 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 34 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN 35 | COMBINATION WITH YOUR PRODUCTS. 36 | 37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR 38 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 39 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION 41 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT 42 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN 43 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | 45 | */ 46 | 47 | #import 48 | #import 49 | #import 50 | #import 51 | #import 52 | #import 53 | #import 54 | #import 55 | #import 56 | #import 57 | 58 | // Allows access to path information associated with tool and plist installation 59 | // from BetterAuthorizationSampleLib.h 60 | #define BAS_PRIVATE 1 61 | 62 | #import "AuthorizationLib/BetterAuthorizationSampleLib.h" 63 | 64 | extern char **environ; 65 | 66 | static int RunLaunchCtl( 67 | bool junkStdIO, 68 | const char *command, 69 | const char *plistPath 70 | ) 71 | // Handles all the invocations of launchctl by doing the fork() + execve() 72 | // for proper clean-up. Only two commands are really supported by our 73 | // implementation; loading and unloading of a job via the plist pointed at 74 | // (const char *) plistPath. 75 | { 76 | int err; 77 | const char * args[5]; 78 | pid_t childPID; 79 | pid_t waitResult; 80 | int status; 81 | 82 | // Pre-conditions. 83 | assert(command != NULL); 84 | assert(plistPath != NULL); 85 | 86 | // Make sure we get sensible logging even if we never get to the waitpid. 87 | 88 | status = 0; 89 | 90 | // Set up the launchctl arguments. We run launchctl using StartupItemContext 91 | // because, in future system software, launchctl may decide on the launchd 92 | // to talk to based on your Mach bootstrap namespace rather than your RUID. 93 | 94 | args[0] = "/bin/launchctl"; 95 | args[1] = command; // "load" or "unload" 96 | args[2] = "-w"; 97 | args[3] = plistPath; // path to plist 98 | args[4] = NULL; 99 | 100 | fprintf(stderr, "launchctl %s %s '%s'\n", args[1], args[2], args[3]); 101 | 102 | // Do the standard fork/exec dance. 103 | 104 | childPID = fork(); 105 | switch (childPID) { 106 | case 0: 107 | // child 108 | err = 0; 109 | 110 | // If we've been told to junk the I/O for launchctl, open 111 | // /dev/null and dup that down to stdin, stdout, and stderr. 112 | 113 | if (junkStdIO) { 114 | int fd; 115 | int err2; 116 | 117 | fd = open("/dev/null", O_RDWR); 118 | if (fd < 0) { 119 | err = errno; 120 | } 121 | if (err == 0) { 122 | if ( dup2(fd, STDIN_FILENO) < 0 ) { 123 | err = errno; 124 | } 125 | } 126 | if (err == 0) { 127 | if ( dup2(fd, STDOUT_FILENO) < 0 ) { 128 | err = errno; 129 | } 130 | } 131 | if (err == 0) { 132 | if ( dup2(fd, STDERR_FILENO) < 0 ) { 133 | err = errno; 134 | } 135 | } 136 | err2 = close(fd); 137 | if (err2 < 0) { 138 | err2 = 0; 139 | } 140 | if (err == 0) { 141 | err = err2; 142 | } 143 | } 144 | if (err == 0) { 145 | err = execve(args[0], (char **) args, environ); 146 | } 147 | if (err < 0) { 148 | err = errno; 149 | } 150 | (void) err; 151 | _exit(EXIT_FAILURE); 152 | break; 153 | case -1: 154 | err = errno; 155 | break; 156 | default: 157 | err = 0; 158 | break; 159 | } 160 | 161 | // Only the parent gets here. Wait for the child to complete and get its 162 | // exit status. 163 | 164 | if (err == 0) { 165 | do { 166 | waitResult = waitpid(childPID, &status, 0); 167 | } while ( (waitResult == -1) && (errno == EINTR) ); 168 | 169 | if (waitResult < 0) { 170 | err = errno; 171 | } else { 172 | assert(waitResult == childPID); 173 | 174 | if ( ! WIFEXITED(status) || (WEXITSTATUS(status) != 0) ) { 175 | err = EINVAL; 176 | } 177 | } 178 | } 179 | 180 | fprintf(stderr, "launchctl -> %d %ld 0x%x\n", err, (long) childPID, status); 181 | 182 | return err; 183 | } 184 | 185 | static int CopyFileOverwriting( 186 | const char *sourcePath, 187 | mode_t destMode, 188 | const char *destPath 189 | ) 190 | // Our own version of a file copy. This routine will either handle 191 | // the copy of the tool binary or the plist file associated with 192 | // that binary. As the function name suggests, it writes over any 193 | // existing file pointed to by (const char *) destPath. 194 | { 195 | int err; 196 | int junk; 197 | int sourceFD; 198 | int destFD; 199 | char buf[65536]; 200 | 201 | // Pre-conditions. 202 | assert(sourcePath != NULL); 203 | assert(destPath != NULL); 204 | 205 | (void) unlink(destPath); 206 | 207 | destFD = -1; 208 | 209 | err = 0; 210 | sourceFD = open(sourcePath, O_RDONLY); 211 | if (sourceFD < 0) { 212 | err = errno; 213 | } 214 | 215 | if (err == 0) { 216 | destFD = open(destPath, O_CREAT | O_EXCL | O_WRONLY, destMode); 217 | if (destFD < 0) { 218 | err = errno; 219 | } 220 | } 221 | 222 | if (err == 0) { 223 | ssize_t bytesReadThisTime; 224 | ssize_t bytesWrittenThisTime; 225 | ssize_t bytesWritten; 226 | 227 | do { 228 | bytesReadThisTime = read(sourceFD, buf, sizeof(buf)); 229 | if (bytesReadThisTime < 0) { 230 | err = errno; 231 | } 232 | 233 | bytesWritten = 0; 234 | while ( (err == 0) && (bytesWritten < bytesReadThisTime) ) { 235 | bytesWrittenThisTime = write(destFD, &buf[bytesWritten], bytesReadThisTime - bytesWritten); 236 | if (bytesWrittenThisTime < 0) { 237 | err = errno; 238 | } else { 239 | bytesWritten += bytesWrittenThisTime; 240 | } 241 | } 242 | 243 | } while ( (err == 0) && (bytesReadThisTime != 0) ); 244 | } 245 | 246 | // Clean up. 247 | 248 | if (sourceFD != -1) { 249 | junk = close(sourceFD); 250 | assert(junk == 0); 251 | } 252 | if (destFD != -1) { 253 | junk = close(destFD); 254 | assert(junk == 0); 255 | } 256 | 257 | fprintf(stderr, "copy '%s' %#o '%s' -> %d\n", sourcePath, (int) destMode, destPath, err); 258 | 259 | return err; 260 | } 261 | 262 | static int InstallCommand( 263 | const char * bundleID, 264 | const char * toolSourcePath, 265 | const char * plistSourcePath 266 | ) 267 | // Heavy lifting function for handling all the necessary steps to install a 268 | // helper tool in the correct location, with the correct permissions, 269 | // and call launchctl in order to load it as a current job. 270 | { 271 | int err; 272 | char toolDestPath[PATH_MAX]; 273 | char plistDestPath[PATH_MAX]; 274 | struct stat sb; 275 | static const mode_t kDirectoryMode = ACCESSPERMS & ~(S_IWGRP | S_IWOTH); 276 | static const mode_t kExecutableMode = ACCESSPERMS & ~(S_IWGRP | S_IWOTH); 277 | static const mode_t kFileMode = DEFFILEMODE & ~(S_IWGRP | S_IWOTH); 278 | 279 | // Pre-conditions. 280 | assert(bundleID != NULL); 281 | assert(toolSourcePath != NULL); 282 | assert(plistSourcePath != NULL); 283 | 284 | (void) snprintf(toolDestPath, sizeof(toolDestPath), kBASToolPathFormat, bundleID); 285 | (void) snprintf(plistDestPath, sizeof(plistDestPath), kBASPlistPathFormat, bundleID); 286 | 287 | // Stop the helper tool if it's currently running. 288 | 289 | (void) RunLaunchCtl(true, "unload", plistDestPath); 290 | 291 | // Create the PrivilegedHelperTools directory. The owner will be "root" because 292 | // we're running as root (our EUID is 0). The group will be "admin" because 293 | // it's inherited from "/Library". The permissions will be rwxr-xr-x because 294 | // of kDirectoryMode combined with our umask. 295 | 296 | err = mkdir(kBASToolDirPath, kDirectoryMode); 297 | if (err < 0) { 298 | err = errno; 299 | } 300 | fprintf(stderr, "mkdir '%s' %#o -> %d\n", kBASToolDirPath, kDirectoryMode, err); 301 | if ( (err == 0) || (err == EEXIST) ) { 302 | err = stat(kBASToolDirPath, &sb); 303 | if (err < 0) { 304 | err = errno; 305 | } 306 | } 307 | 308 | // /Library/PrivilegedHelperTools may have come from a number of places: 309 | // 310 | // A. We may have just created it. In this case it will be 311 | // root:admin rwxr-xr-x. 312 | // 313 | // B. It may have been correctly created by someone else. By definition, 314 | // that makes it root:wheel rwxr-xr-x. 315 | // 316 | // C. It may have been created (or moved here) incorrectly (or maliciously) 317 | // by someone else. In that case it will be u:g xxxxxxxxx, where u is 318 | // not root, or root:g xxxxwxxwx (that is, root-owned by writeable by 319 | // someone other than root). 320 | // 321 | // In case A, we want to correct the group. In case B, we want to do 322 | // nothing. In case C, we want to fail. 323 | 324 | if (err == 0) { 325 | if ( (sb.st_uid == 0) && (sb.st_gid == 0) ) { 326 | // case B -- do nothing 327 | } else if ( (sb.st_uid == 0) && (sb.st_gid != 0) && ((sb.st_mode & ALLPERMS) == kDirectoryMode) ) { 328 | // case A -- fix the group ID 329 | // 330 | // This is safe because /Library is sticky and the file is owned 331 | // by root, which means that only root can move it. Also, we 332 | // don't have to worry about malicious files existing within the 333 | // directory because its only writeable by root. 334 | 335 | err = chown(kBASToolDirPath, -1, 0); 336 | if (err < 0) { 337 | err = errno; 338 | } 339 | fprintf(stderr, "chown -1:0 '%s' -> %d\n", kBASToolDirPath, err); 340 | } else { 341 | fprintf(stderr, "bogus perms on '%s' %d:%d %o\n", kBASToolDirPath, (int) sb.st_uid, (int) sb.st_gid, (int) sb.st_mode); 342 | err = EPERM; 343 | } 344 | } 345 | 346 | // Then create the known good copy. The ownership and permissions 347 | // will be set appropriately, as described in the comments for mkdir. 348 | // We don't have to worry about atomicity because this tool won't be 349 | // looked at until our plist is installed. 350 | 351 | if (err == 0) { 352 | err = CopyFileOverwriting(toolSourcePath, kExecutableMode, toolDestPath); 353 | } 354 | 355 | // For the plist, our caller has created the file in /tmp and we just copy it 356 | // into the correct location. This ensures that the file is complete 357 | // and valid before anyone starts looking at it and will also overwrite 358 | // any existing file with this new version. 359 | // 360 | // Since we have to read/write in the file byte by byte to make sure that 361 | // the file is complete we are rolling our own 'copy'. This clearly is 362 | // ignoring atomicity since we do not roll back to the state of 'what was 363 | // previously there' if there is an error; rather, whatever has been 364 | // written up to that point of granular failure /is/ the state of the 365 | // plist file. 366 | 367 | if (err == 0) { 368 | err = CopyFileOverwriting(plistSourcePath, kFileMode, plistDestPath); 369 | } 370 | 371 | // Use launchctl to load our job. The plist file starts out disabled, 372 | // so we pass "-w" to enable it permanently. 373 | 374 | if (err == 0) { 375 | err = RunLaunchCtl(false, "load", plistDestPath); 376 | } 377 | 378 | return err; 379 | } 380 | 381 | static int EnableCommand( 382 | const char *bundleID 383 | ) 384 | // Utility function to call through to RunLaunchCtl in order to load a job 385 | // given by the path contructed from the (const char *) bundleID. 386 | { 387 | int err; 388 | char plistPath[PATH_MAX]; 389 | 390 | // Pre-condition. 391 | assert(bundleID != NULL); 392 | 393 | (void) snprintf(plistPath, sizeof(plistPath), kBASPlistPathFormat, bundleID); 394 | err = RunLaunchCtl(false, "load", plistPath); 395 | 396 | return err; 397 | } 398 | 399 | int main(int argc, char **argv) 400 | { 401 | int err; 402 | 403 | // Print our PID so that the app can avoid creating zombies. 404 | 405 | fprintf(stdout, kBASAntiZombiePIDToken1 "%ld" kBASAntiZombiePIDToken2 "\n", (long) getpid()); 406 | fflush(stdout); 407 | 408 | // On the client side, AEWP only gives a handle to stdout, so we dup stdout 409 | // downto stderr for the rest of this tool. This ensures that all our output 410 | // makes it to the client. 411 | 412 | err = dup2(STDOUT_FILENO, STDERR_FILENO); 413 | if (err < 0) { 414 | err = errno; 415 | } else { 416 | err = 0; 417 | } 418 | 419 | // Set up the standard umask. The goal here is to be robust in the 420 | // face of common environmental changes, not to resist a malicious attack. 421 | // Also sync the RUID to the 0 because launchctl keys off the RUID (at least 422 | // on 10.4.x). 423 | 424 | if (err == 0) { 425 | (void) umask(S_IWGRP | S_IWOTH); 426 | 427 | err = setuid(0); 428 | if (err < 0) { 429 | fprintf(stderr, "setuid\n"); 430 | err = EINVAL; 431 | } 432 | } 433 | 434 | if ( (err == 0) && (argc < 2) ) { 435 | fprintf(stderr, "usage\n"); 436 | err = EINVAL; 437 | } 438 | 439 | // The first argument is the command. Switch off that and extract the 440 | // remaining arguments and pass them to our command routines. 441 | 442 | if (err == 0) { 443 | if ( strcmp(argv[1], kBASInstallToolInstallCommand) == 0 ) { 444 | if (argc == 5) { 445 | err = InstallCommand(argv[2], argv[3], argv[4]); 446 | } else { 447 | fprintf(stderr, "usage3\n"); 448 | err = EINVAL; 449 | } 450 | } else if ( strcmp(argv[1], kBASInstallToolEnableCommand) == 0 ) { 451 | if (argc == 3) { 452 | err = EnableCommand(argv[2]); 453 | } else { 454 | fprintf(stderr, "usage4\n"); 455 | err = EINVAL; 456 | } 457 | } else { 458 | fprintf(stderr, "usage2\n"); 459 | err = EINVAL; 460 | } 461 | } 462 | 463 | // Write "oK" to stdout and quit. The presence of the "oK" on the last 464 | // line of output is used by the calling code to detect success. 465 | 466 | if (err == 0) { 467 | fprintf(stderr, kBASInstallToolSuccess "\n"); 468 | } else { 469 | fprintf(stderr, kBASInstallToolFailure "\n", err); 470 | } 471 | 472 | return (err == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 473 | } 474 | -------------------------------------------------------------------------------- /HelperTool/TediumHelperTool.c: -------------------------------------------------------------------------------- 1 | // 2 | // HelperTool.c 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/16/2012. 6 | // Copyright 2012. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | #import "AuthorizationLib/BetterAuthorizationSampleLib.h" 17 | #import "TediumHelpertoolCommon.h" 18 | 19 | extern const BASCommandSpec kTediumHelperToolCommandSet[]; 20 | 21 | 22 | // Implements the GetVersionCommand. Returns the version number of the helper tool. 23 | static OSStatus DoGetVersion(AuthorizationRef auth, 24 | const void * userData, 25 | CFDictionaryRef request, 26 | CFMutableDictionaryRef response, 27 | aslclient asl, 28 | aslmsg aslMsg) { 29 | 30 | OSStatus retval = noErr; 31 | CFNumberRef value; 32 | static const unsigned int kCurrentVersion = kTediumHelperToolVersionNumber; 33 | 34 | assert(auth != NULL); 35 | assert(request != NULL); 36 | assert(response != NULL); 37 | 38 | syslog(LOG_DEBUG,"Getting Helper Tool Version"); 39 | // Add to the response. 40 | value = CFNumberCreate(NULL, kCFNumberIntType, &kCurrentVersion); 41 | if (!value) 42 | retval = coreFoundationUnknownErr; 43 | else { 44 | CFDictionaryAddValue(response, CFSTR(kTediumHelperToolGetVersionResponse), value); 45 | CFRelease(value); 46 | } 47 | 48 | return retval; 49 | } 50 | 51 | 52 | // Implements the SetAFPDestination. 53 | static OSStatus DoSetAFPDestination(AuthorizationRef auth, 54 | const void * userData, 55 | CFDictionaryRef request, 56 | CFMutableDictionaryRef response, 57 | aslclient asl, 58 | aslmsg aslMsg) { 59 | 60 | OSStatus retval = noErr; 61 | 62 | assert(auth != NULL); 63 | assert(request != NULL); 64 | assert(response != NULL); 65 | 66 | CFDictionaryRef parameters = (CFDictionaryRef) CFDictionaryGetValue(request, CFSTR("param")); 67 | CFStringRef uname = (CFStringRef) CFDictionaryGetValue(parameters, CFSTR("username")); 68 | CFStringRef pwd = (CFStringRef) CFDictionaryGetValue(parameters, CFSTR("password")); 69 | CFStringRef host = (CFStringRef) CFDictionaryGetValue(parameters, CFSTR("hostname")); 70 | CFStringRef url = (CFStringRef) CFDictionaryGetValue(parameters, CFSTR("url")); 71 | 72 | 73 | if (uname == NULL) 74 | return BASErrnoToOSStatus(EINVAL); 75 | 76 | if (pwd == NULL) 77 | return BASErrnoToOSStatus(EINVAL); 78 | 79 | if (!host) 80 | return BASErrnoToOSStatus(EINVAL); 81 | 82 | if (!url) 83 | return BASErrnoToOSStatus(EINVAL); 84 | 85 | char command[1024]; 86 | char hostname[1024]; 87 | char username[1024]; 88 | char password[1024]; 89 | char theUrl[1024]; 90 | 91 | Boolean success; 92 | 93 | success = CFStringGetCString(uname, username, 1024, kCFStringEncodingUTF8); 94 | 95 | if (!success) 96 | return BASErrnoToOSStatus(EINVAL);; 97 | 98 | success = CFStringGetCString(pwd, password, 1024, kCFStringEncodingUTF8); 99 | 100 | if (!success) 101 | return BASErrnoToOSStatus(EINVAL); 102 | 103 | success = CFStringGetCString(host, hostname, 1024, kCFStringEncodingUTF8); 104 | 105 | if (!success) 106 | return BASErrnoToOSStatus(EINVAL); 107 | 108 | success = CFStringGetCString(url, theUrl, 1024, kCFStringEncodingUTF8); 109 | 110 | if (!success) 111 | return BASErrnoToOSStatus(EINVAL); 112 | 113 | sprintf(command, "/usr/bin/tmutil setdestination afp://\"%s\":\"%s\"@\"%s%s\"", username, password, hostname, theUrl); 114 | 115 | retval = system(command); 116 | return retval; 117 | 118 | } 119 | 120 | // Implements the SetDestination. 121 | static OSStatus DoSetDestination(AuthorizationRef auth, 122 | const void * userData, 123 | CFDictionaryRef request, 124 | CFMutableDictionaryRef response, 125 | aslclient asl, 126 | aslmsg aslMsg) { 127 | 128 | OSStatus retval = noErr; 129 | 130 | 131 | 132 | assert(auth != NULL); 133 | assert(request != NULL); 134 | assert(response != NULL); 135 | 136 | CFStringRef parameter = (CFStringRef) CFDictionaryGetValue(request, CFSTR("param")); 137 | 138 | char command[256]; 139 | char parameters[1024]; 140 | 141 | CFStringGetCString(parameter,parameters,1024, kCFStringEncodingUTF8); 142 | 143 | //syslog(LOG_EMERG,"running command with '%s'", parameters); 144 | sprintf(command, "/usr/bin/tmutil setdestination '%s'", parameters); 145 | retval = system(command); 146 | //syslog(LOG_EMERG, "command finished"); 147 | return retval; 148 | 149 | } 150 | 151 | // Implements the SetMobileBackup. Returns the version number of the helper tool. 152 | static OSStatus DoSetMobileBackup(AuthorizationRef auth, 153 | const void * userData, 154 | CFDictionaryRef request, 155 | CFMutableDictionaryRef response, 156 | aslclient asl, 157 | aslmsg aslMsg) { 158 | 159 | OSStatus retval = noErr; 160 | int value; 161 | 162 | 163 | assert(auth != NULL); 164 | assert(request != NULL); 165 | assert(response != NULL); 166 | 167 | CFNumberRef parameter = (CFNumberRef) CFDictionaryGetValue(request, CFSTR("param")); 168 | 169 | char command[256]; 170 | 171 | CFNumberGetValue(parameter, kCFNumberSInt32Type, &value); 172 | 173 | 174 | if (value) 175 | sprintf(command, "/usr/bin/tmutil enablelocal"); 176 | else 177 | sprintf(command, "/usr/bin/tmutil disablelocal"); 178 | 179 | retval = system(command); 180 | //syslog(LOG_EMERG, "command finished"); 181 | return retval; 182 | 183 | } 184 | 185 | // Implements the SetMobileBackup. Returns the version number of the helper tool. 186 | static OSStatus DoMobileBackupNow(AuthorizationRef auth, 187 | const void * userData, 188 | CFDictionaryRef request, 189 | CFMutableDictionaryRef response, 190 | aslclient asl, 191 | aslmsg aslMsg) { 192 | 193 | OSStatus retval = noErr; 194 | 195 | assert(auth != NULL); 196 | assert(request != NULL); 197 | assert(response != NULL); 198 | 199 | 200 | char command[256]; 201 | 202 | sprintf(command, "/usr/bin/tmutil snapshot"); 203 | 204 | retval = system(command); 205 | //syslog(LOG_EMERG, "command finished"); 206 | return retval; 207 | 208 | } 209 | 210 | // Implements the SetMobileBackup. Returns the version number of the helper tool. 211 | static OSStatus DoBackupNow(AuthorizationRef auth, 212 | const void * userData, 213 | CFDictionaryRef request, 214 | CFMutableDictionaryRef response, 215 | aslclient asl, 216 | aslmsg aslMsg) { 217 | 218 | OSStatus retval = noErr; 219 | 220 | 221 | assert(auth != NULL); 222 | assert(request != NULL); 223 | assert(response != NULL); 224 | 225 | char command[256]; 226 | 227 | 228 | 229 | sprintf(command, "/usr/bin/tmutil startbackup"); 230 | 231 | 232 | retval = system(command); 233 | //syslog(LOG_EMERG, "command finished"); 234 | return retval; 235 | 236 | } 237 | 238 | #pragma mark - 239 | #pragma mark Tool Infrastructure 240 | 241 | // the list defined here must match (same order) the list in CPHelperToolCommon.c 242 | static const BASCommandProc kHelperToolCommandProcs[] = { 243 | DoGetVersion, 244 | DoSetAFPDestination, 245 | DoSetDestination, 246 | DoSetMobileBackup, 247 | DoMobileBackupNow, 248 | DoBackupNow, 249 | NULL 250 | }; 251 | 252 | int main(int argc, char **argv) { 253 | // Go directly into BetterAuthorizationSampleLib code. 254 | 255 | // IMPORTANT 256 | // BASHelperToolMain doesn't clean up after itself, so once it returns 257 | // we must quit. 258 | 259 | return BASHelperToolMain(kTediumHelperToolCommandSet, kHelperToolCommandProcs); 260 | } 261 | -------------------------------------------------------------------------------- /HelperTool/TediumHelperToolCommon.c: -------------------------------------------------------------------------------- 1 | // 2 | // HelperToolCommon.c 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/16/2012. 6 | // Copyright 2012. All rights reserved. 7 | // 8 | 9 | #import "TediumHelpertoolCommon.h" 10 | #import "BetterAuthorizationSampleLib.h" 11 | 12 | const BASCommandSpec kTediumHelperToolCommandSet[] = { 13 | { kTediumHelperToolGetVersionCommand, // commandName 14 | NULL, // rightName 15 | NULL, // rightDefaultRule 16 | NULL, // rightDescriptionKey 17 | NULL // userData 18 | }, 19 | { kTediumHelperToolSetAFPDestinationCommand, 20 | kTediumHelperToolSetAFPDestinationCommandRight, 21 | "allow", 22 | "SetAFPDestination", 23 | NULL 24 | }, 25 | { kTediumHelperToolSetDestinationCommand, 26 | kTediumHelperToolSetDestinationCommandRight, 27 | "allow", 28 | "SetDestination", 29 | NULL 30 | }, 31 | { kTediumHelperToolSetMobileBackupCommand, 32 | kTediumHelperToolSetMobileBackupCommandRight, 33 | "allow", 34 | "SetMobileBackup", 35 | NULL 36 | }, 37 | { kTediumHelperToolMobileBackupNowCommand, 38 | kTediumHelperToolMobileBackupNowCommandRight, 39 | "allow", 40 | "MobileBackupNow", 41 | NULL 42 | }, 43 | { kTediumHelperToolBackupNowCommand, 44 | kTediumHelperToolBackupNowCommandRight, 45 | "allow", 46 | "BackupNow", 47 | NULL 48 | }, 49 | { NULL, 50 | NULL, 51 | NULL, 52 | NULL, 53 | NULL 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /HelperTool/TediumHelpertoolCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // HelperToolCommon.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/16/2012. 6 | // Copyright 2012. All rights reserved. 7 | // 8 | 9 | #ifndef Tedium_HelperToolCommon_h 10 | #define Tedium_HelperToolCommon_h 11 | 12 | #import "BetterAuthorizationSampleLib.h" 13 | 14 | // Helper tool version 15 | #define kTediumHelperToolVersionNumber 6 16 | 17 | // Commands 18 | #define kTediumHelperToolGetVersionCommand "GetVersion" 19 | #define kTediumHelperToolGetVersionResponse "Version" 20 | 21 | #define kTediumHelperToolSetAFPDestinationCommand "SetAFPDestination" 22 | #define kTediumHelperToolSetDestinationCommand "SetDestination" 23 | #define kTediumHelperToolSetMobileBackupCommand "SetMobileBackup" 24 | 25 | #define kTediumHelperToolMobileBackupNowCommand "MobileBackupNow" 26 | #define kTediumHelperToolBackupNowCommand "BackupNow" 27 | 28 | #define kTediumHelperToolSetAFPDestinationCommandRight "com.dustinrue.Tedium.SetAFPDestinationCommandRight" 29 | #define kTediumHelperToolSetDestinationCommandRight "com.dustinrue.Tedium.SetDestinationCommandRight" 30 | #define kTediumHelperToolSetMobileBackupCommandRight "com.dustinrue.Tedium.SetMobileBackupCommandRight" 31 | #define kTediumHelperToolMobileBackupNowCommandRight "com.dustinrue.Tedium.MobileBackupNowCommandRight" 32 | #define kTediumHelperToolBackupNowCommandRight "com.dustinrue.Tedium.BackupNowCommandRight" 33 | 34 | // Commands array (keep in sync!) 35 | extern const BASCommandSpec kTediumHelperToolCommandSet[]; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tedium 2 | ============ 3 | 4 | What is Tedium 5 | -------------------- 6 | 7 | Tedium allows you to switch Time Machine backup destinations easily. 8 | 9 | How to Get Tedium 10 | ----------------------- 11 | 12 | Tedium is no longer available for download and must be build from source. 13 | 14 | Building Tedium from Source 15 | --------------------------------- 16 | 17 | Tedium is free, open source software hosted at . If you wish to build Tedium yourself you can do so by cloning the Tedium code to your computer. If you don't have git installed you will need to install either the GitHub client for Mac at which install a GUI client as well as the command line client or if you simply want the command line client installed you can get it from . If you need additional help with using Git GitHub is a great resource. You will find GitHub's documentation at . 18 | 19 | 20 | Once cloned, open the project file in Xcode and issue command+b to build the app or command+r to build and run it. 21 | -------------------------------------------------------------------------------- /Resources/Tedium.sdef: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Resources/awesomeclock.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Resources/awesomeclock.icns -------------------------------------------------------------------------------- /Resources/dsa_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOBdDqSjeeQcjxI1O+Yr57a2MV9SWxiL 3 | UYTOwLy4OmieXYbaVfBsziMqSAVjlD1fiRLIJ0drga69ZqhHQBLPSqcoGJXUVTPv 4 | PSJgp5MwzEUEUH+h+sdGZOofV8M6+tjJAUUg57mEcN6v4040dsM0C5Dzd50Y/KHj 5 | h+5FCjSmqXzLAhUA1D+eE/V9fyxG1zalKyK04PZLJk8CgYEA1xQLixooZjkvmg3H 6 | CkvfqILlOmMbz+mvP0MEVig4qZUlZ1BshWzmbQB+6ByCfjhmOy+3QmuhQ2EjCa5a 7 | vcNPGOsB/j6nBvAz7oSqEVe+ZRxOoRfr7QIM8SjlJCxfu+gHUFHAeypK9nhUqHdB 8 | yeWnGaU4M5ouEI4+sEg32u1GyvADgYUAAoGBAMg339rwEDA0Nlwu6AUfFU6PNG+X 9 | 32GLFeDehBCIO2tuaIVfL/7nOFAUGL7f6/RspNg9U7ixqvBGkKXzYLQDyEd9x1M+ 10 | 3iPoQEMtcOLBNF2YQfKFWloS52TqJiI5pnMS+WcX3Gmuivdl9MBaeuHdBpSvveY1 11 | i9EGAN6Az3XcYUL+ 12 | -----END PUBLIC KEY----- 13 | -------------------------------------------------------------------------------- /Resources/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Resources/gear.png -------------------------------------------------------------------------------- /Resources/minus-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Resources/minus-8.png -------------------------------------------------------------------------------- /Resources/plus-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Resources/plus-arrow.png -------------------------------------------------------------------------------- /Sparkle.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Sparkle.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Sparkle.framework/Sparkle: -------------------------------------------------------------------------------- 1 | Versions/Current/Sparkle -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/SUAppcast.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUAppcast.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/12/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUAPPCAST_H 10 | #define SUAPPCAST_H 11 | 12 | @class SUAppcastItem; 13 | @interface SUAppcast : NSObject 14 | { 15 | @private 16 | NSArray *items; 17 | NSString *userAgentString; 18 | id delegate; 19 | NSString *downloadFilename; 20 | NSURLDownload *download; 21 | } 22 | 23 | - (void)fetchAppcastFromURL:(NSURL *)url; 24 | - (void)setDelegate:delegate; 25 | - (void)setUserAgentString:(NSString *)userAgentString; 26 | 27 | - (NSArray *)items; 28 | 29 | @end 30 | 31 | @interface NSObject (SUAppcastDelegate) 32 | - (void)appcastDidFinishLoading:(SUAppcast *)appcast; 33 | - (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error; 34 | @end 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/SUAppcastItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUAppcastItem.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/12/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUAPPCASTITEM_H 10 | #define SUAPPCASTITEM_H 11 | 12 | @interface SUAppcastItem : NSObject 13 | { 14 | @private 15 | NSString *title; 16 | NSDate *date; 17 | NSString *itemDescription; 18 | 19 | NSURL *releaseNotesURL; 20 | 21 | NSString *DSASignature; 22 | NSString *minimumSystemVersion; 23 | 24 | NSURL *fileURL; 25 | NSString *versionString; 26 | NSString *displayVersionString; 27 | 28 | NSDictionary *deltaUpdates; 29 | 30 | NSDictionary *propertiesDictionary; 31 | 32 | NSURL *infoURL; // UK 2007-08-31 33 | } 34 | 35 | // Initializes with data from a dictionary provided by the RSS class. 36 | - initWithDictionary:(NSDictionary *)dict; 37 | - initWithDictionary:(NSDictionary *)dict failureReason:(NSString**)error; 38 | 39 | - (NSString *)title; 40 | - (NSString *)versionString; 41 | - (NSString *)displayVersionString; 42 | - (NSDate *)date; 43 | - (NSString *)itemDescription; 44 | - (NSURL *)releaseNotesURL; 45 | - (NSURL *)fileURL; 46 | - (NSString *)DSASignature; 47 | - (NSString *)minimumSystemVersion; 48 | - (NSDictionary *)deltaUpdates; 49 | - (BOOL)isDeltaUpdate; 50 | 51 | // Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. 52 | - (NSDictionary *)propertiesDictionary; 53 | 54 | - (NSURL *)infoURL; // UK 2007-08-31 55 | 56 | @end 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/SUUpdater.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUUpdater.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 1/4/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUUPDATER_H 10 | #define SUUPDATER_H 11 | 12 | #import "SUVersionComparisonProtocol.h" 13 | #import "SUVersionDisplayProtocol.h" 14 | 15 | @class SUUpdateDriver, SUAppcastItem, SUHost, SUAppcast; 16 | 17 | @interface SUUpdater : NSObject 18 | { 19 | @private 20 | NSTimer *checkTimer; 21 | SUUpdateDriver *driver; 22 | 23 | NSString *customUserAgentString; 24 | SUHost *host; 25 | IBOutlet id delegate; 26 | } 27 | 28 | + (SUUpdater *)sharedUpdater; 29 | + (SUUpdater *)updaterForBundle:(NSBundle *)bundle; 30 | - initForBundle:(NSBundle *)bundle; 31 | 32 | - (NSBundle *)hostBundle; 33 | 34 | - (void)setDelegate:(id)delegate; 35 | - delegate; 36 | 37 | - (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyChecks; 38 | - (BOOL)automaticallyChecksForUpdates; 39 | 40 | - (void)setUpdateCheckInterval:(NSTimeInterval)interval; 41 | - (NSTimeInterval)updateCheckInterval; 42 | 43 | - (void)setFeedURL:(NSURL *)feedURL; 44 | - (NSURL *)feedURL; // *** MUST BE CALLED ON MAIN THREAD *** 45 | 46 | - (void)setUserAgentString:(NSString *)userAgent; 47 | - (NSString *)userAgentString; 48 | 49 | - (void)setSendsSystemProfile:(BOOL)sendsSystemProfile; 50 | - (BOOL)sendsSystemProfile; 51 | 52 | - (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyDownloadsUpdates; 53 | - (BOOL)automaticallyDownloadsUpdates; 54 | 55 | // This IBAction is meant for a main menu item. Hook up any menu item to this action, 56 | // and Sparkle will check for updates and report back its findings verbosely. 57 | - (IBAction)checkForUpdates:(id)sender; 58 | 59 | // This kicks off an update meant to be programmatically initiated. That is, it will display no UI unless it actually finds an update, 60 | // in which case it proceeds as usual. If the fully automated updating is turned on, however, this will invoke that behavior, and if an 61 | // update is found, it will be downloaded and prepped for installation. 62 | - (void)checkForUpdatesInBackground; 63 | 64 | // Date of last update check. Returns nil if no check has been performed. 65 | - (NSDate*)lastUpdateCheckDate; 66 | 67 | // This begins a "probing" check for updates which will not actually offer to update to that version. The delegate methods, though, 68 | // (up to updater:didFindValidUpdate: and updaterDidNotFindUpdate:), are called, so you can use that information in your UI. 69 | - (void)checkForUpdateInformation; 70 | 71 | // Call this to appropriately schedule or cancel the update checking timer according to the preferences for time interval and automatic checks. This call does not change the date of the next check, but only the internal NSTimer. 72 | - (void)resetUpdateCycle; 73 | 74 | - (BOOL)updateInProgress; 75 | 76 | @end 77 | 78 | 79 | // ----------------------------------------------------------------------------- 80 | // SUUpdater Delegate: 81 | // ----------------------------------------------------------------------------- 82 | 83 | @interface NSObject (SUUpdaterDelegateInformalProtocol) 84 | 85 | // Use this to keep Sparkle from popping up e.g. while your setup assistant is showing: 86 | - (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle; 87 | 88 | // This method allows you to add extra parameters to the appcast URL, potentially based on whether or not Sparkle will also be sending along the system profile. This method should return an array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. 89 | - (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; 90 | 91 | // Override this to dynamically specify the entire URL. 92 | - (NSString*)feedURLStringForUpdater:(SUUpdater*)updater; 93 | 94 | // Use this to override the default behavior for Sparkle prompting the user about automatic update checks. 95 | - (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)bundle; 96 | 97 | // Implement this if you want to do some special handling with the appcast once it finishes loading. 98 | - (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; 99 | 100 | // If you're using special logic or extensions in your appcast, implement this to use your own logic for finding 101 | // a valid update, if any, in the given appcast. 102 | - (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)bundle; 103 | 104 | // Sent when a valid update is found by the update driver. 105 | - (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update; 106 | 107 | // Sent when a valid update is not found. 108 | - (void)updaterDidNotFindUpdate:(SUUpdater *)update; 109 | 110 | // Sent immediately before installing the specified update. 111 | - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update; 112 | 113 | // Return YES to delay the relaunch until you do some processing; invoke the given NSInvocation to continue. 114 | // This is not called if the user didn't relaunch on the previous update, in that case it will immediately 115 | // restart. 116 | - (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation; 117 | 118 | // Some apps *can not* be relaunched in certain circumstances. They can use this method 119 | // to prevent a relaunch "hard": 120 | - (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; 121 | 122 | // Called immediately before relaunching. 123 | - (void)updaterWillRelaunchApplication:(SUUpdater *)updater; 124 | 125 | // This method allows you to provide a custom version comparator. 126 | // If you don't implement this method or return nil, the standard version comparator will be used. 127 | - (id )versionComparatorForUpdater:(SUUpdater *)updater; 128 | 129 | // This method allows you to provide a custom version comparator. 130 | // If you don't implement this method or return nil, the standard version displayer will be used. 131 | - (id )versionDisplayerForUpdater:(SUUpdater *)updater; 132 | 133 | // Returns the path which is used to relaunch the client after the update is installed. By default, the path of the host bundle. 134 | - (NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; 135 | 136 | // Called before and after, respectively, an updater shows a modal alert window, to give the host 137 | // the opportunity to hide attached windows etc. that may get in the way: 138 | -(void) updaterWillShowModalAlert:(SUUpdater *)updater; 139 | -(void) updaterDidShowModalAlert:(SUUpdater *)updater; 140 | 141 | @end 142 | 143 | 144 | // ----------------------------------------------------------------------------- 145 | // Constants: 146 | // ----------------------------------------------------------------------------- 147 | 148 | // Define some minimum intervals to avoid DOS-like checking attacks. These are in seconds. 149 | #if defined(DEBUG) && DEBUG && 0 150 | #define SU_MIN_CHECK_INTERVAL 60 151 | #else 152 | #define SU_MIN_CHECK_INTERVAL 60*60 153 | #endif 154 | 155 | #if defined(DEBUG) && DEBUG && 0 156 | #define SU_DEFAULT_CHECK_INTERVAL 60 157 | #else 158 | #define SU_DEFAULT_CHECK_INTERVAL 60*60*24 159 | #endif 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUVersionComparisonProtocol.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 12/21/07. 6 | // Copyright 2007 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUVERSIONCOMPARISONPROTOCOL_H 10 | #define SUVERSIONCOMPARISONPROTOCOL_H 11 | 12 | #import 13 | 14 | /*! 15 | @protocol 16 | @abstract Implement this protocol to provide version comparison facilities for Sparkle. 17 | */ 18 | @protocol SUVersionComparison 19 | 20 | /*! 21 | @method 22 | @abstract An abstract method to compare two version strings. 23 | @discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent. 24 | */ 25 | - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! 26 | 27 | @end 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUVersionDisplayProtocol.h 3 | // EyeTV 4 | // 5 | // Created by Uli Kusterer on 08.12.09. 6 | // Copyright 2009 Elgato Systems GmbH. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | @protocol 14 | @abstract Implement this protocol to apply special formatting to the two 15 | version numbers. 16 | */ 17 | @protocol SUVersionDisplay 18 | 19 | /*! 20 | @method 21 | @abstract An abstract method to format two version strings. 22 | @discussion You get both so you can display important distinguishing 23 | information, but leave out unnecessary/confusing parts. 24 | */ 25 | -(void) formatVersion: (NSString**)inOutVersionA andVersion: (NSString**)inOutVersionB; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Headers/Sparkle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Sparkle.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SPARKLE_H 10 | #define SPARKLE_H 11 | 12 | // This list should include the shared headers. It doesn't matter if some of them aren't shared (unless 13 | // there are name-space collisions) so we can list all of them to start with: 14 | 15 | #import 16 | 17 | #import 18 | #import 19 | #import 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 11C74 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | Sparkle 11 | CFBundleIdentifier 12 | org.andymatuschak.Sparkle 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Sparkle 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.5 Beta (git) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | aca8cc6 25 | DTCompiler 26 | com.apple.compilers.llvm.clang.1_0 27 | DTPlatformBuild 28 | 4D502 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 11C63 33 | DTSDKName 34 | macosx10.7 35 | DTXcode 36 | 0421 37 | DTXcodeBuild 38 | 4D502 39 | 40 | 41 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Andy Matuschak 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | ================= 10 | EXTERNAL LICENSES 11 | ================= 12 | 13 | License for bspatch.c and bsdiff.c, from bsdiff 4.3 (: 14 | /*- 15 | * Copyright 2003-2005 Colin Percival 16 | * All rights reserved 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted providing that the following conditions 20 | * are met: 21 | * 1. Redistributions of source code must retain the above copyright 22 | * notice, this list of conditions and the following disclaimer. 23 | * 2. Redistributions in binary form must reproduce the above copyright 24 | * notice, this list of conditions and the following disclaimer in the 25 | * documentation and/or other materials provided with the distribution. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 31 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 35 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 36 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | */ 39 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ADP2,1 6 | Developer Transition Kit 7 | iMac1,1 8 | iMac G3 (Rev A-D) 9 | iMac4,1 10 | iMac (Core Duo) 11 | iMac4,2 12 | iMac for Education (17-inch, Core Duo) 13 | iMac5,1 14 | iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) 15 | iMac5,2 16 | iMac (Core 2 Duo, 17 inch, Combo Drive) 17 | iMac6,1 18 | iMac (Core 2 Duo, 24 inch, SuperDrive) 19 | iMac8,1 20 | iMac (April 2008) 21 | MacBook1,1 22 | MacBook (Core Duo) 23 | MacBook2,1 24 | MacBook (Core 2 Duo) 25 | MacBook4,1 26 | MacBook (Core 2 Duo Feb 2008) 27 | MacBookAir1,1 28 | MacBook Air (January 2008) 29 | MacBookAir2,1 30 | MacBook Air (June 2009) 31 | MacBookAir3,1 32 | MacBook Air (October 2010) 33 | MacBookPro1,1 34 | MacBook Pro Core Duo (15-inch) 35 | MacBookPro1,2 36 | MacBook Pro Core Duo (17-inch) 37 | MacBookPro2,1 38 | MacBook Pro Core 2 Duo (17-inch) 39 | MacBookPro2,2 40 | MacBook Pro Core 2 Duo (15-inch) 41 | MacBookPro3,1 42 | MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) 43 | MacBookPro3,2 44 | MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) 45 | MacBookPro4,1 46 | MacBook Pro (Core 2 Duo Feb 2008) 47 | Macmini1,1 48 | Mac Mini (Core Solo/Duo) 49 | MacPro1,1 50 | Mac Pro (four-core) 51 | MacPro2,1 52 | Mac Pro (eight-core) 53 | MacPro3,1 54 | Mac Pro (January 2008 4- or 8- core "Harpertown") 55 | MacPro4,1 56 | Mac Pro (March 2009) 57 | MacPro5,1 58 | Mac Pro (August 2010) 59 | PowerBook1,1 60 | PowerBook G3 61 | PowerBook2,1 62 | iBook G3 63 | PowerBook2,2 64 | iBook G3 (FireWire) 65 | PowerBook2,3 66 | iBook G3 67 | PowerBook2,4 68 | iBook G3 69 | PowerBook3,1 70 | PowerBook G3 (FireWire) 71 | PowerBook3,2 72 | PowerBook G4 73 | PowerBook3,3 74 | PowerBook G4 (Gigabit Ethernet) 75 | PowerBook3,4 76 | PowerBook G4 (DVI) 77 | PowerBook3,5 78 | PowerBook G4 (1GHz / 867MHz) 79 | PowerBook4,1 80 | iBook G3 (Dual USB, Late 2001) 81 | PowerBook4,2 82 | iBook G3 (16MB VRAM) 83 | PowerBook4,3 84 | iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) 85 | PowerBook5,1 86 | PowerBook G4 (17 inch) 87 | PowerBook5,2 88 | PowerBook G4 (15 inch FW 800) 89 | PowerBook5,3 90 | PowerBook G4 (17-inch 1.33GHz) 91 | PowerBook5,4 92 | PowerBook G4 (15 inch 1.5/1.33GHz) 93 | PowerBook5,5 94 | PowerBook G4 (17-inch 1.5GHz) 95 | PowerBook5,6 96 | PowerBook G4 (15 inch 1.67GHz/1.5GHz) 97 | PowerBook5,7 98 | PowerBook G4 (17-inch 1.67GHz) 99 | PowerBook5,8 100 | PowerBook G4 (Double layer SD, 15 inch) 101 | PowerBook5,9 102 | PowerBook G4 (Double layer SD, 17 inch) 103 | PowerBook6,1 104 | PowerBook G4 (12 inch) 105 | PowerBook6,2 106 | PowerBook G4 (12 inch, DVI) 107 | PowerBook6,3 108 | iBook G4 109 | PowerBook6,4 110 | PowerBook G4 (12 inch 1.33GHz) 111 | PowerBook6,5 112 | iBook G4 (Early-Late 2004) 113 | PowerBook6,7 114 | iBook G4 (Mid 2005) 115 | PowerBook6,8 116 | PowerBook G4 (12 inch 1.5GHz) 117 | PowerMac1,1 118 | Power Macintosh G3 (Blue & White) 119 | PowerMac1,2 120 | Power Macintosh G4 (PCI Graphics) 121 | PowerMac10,1 122 | Mac Mini G4 123 | PowerMac10,2 124 | Mac Mini (Late 2005) 125 | PowerMac11,2 126 | Power Macintosh G5 (Late 2005) 127 | PowerMac12,1 128 | iMac G5 (iSight) 129 | PowerMac2,1 130 | iMac G3 (Slot-loading CD-ROM) 131 | PowerMac2,2 132 | iMac G3 (Summer 2000) 133 | PowerMac3,1 134 | Power Macintosh G4 (AGP Graphics) 135 | PowerMac3,2 136 | Power Macintosh G4 (AGP Graphics) 137 | PowerMac3,3 138 | Power Macintosh G4 (Gigabit Ethernet) 139 | PowerMac3,4 140 | Power Macintosh G4 (Digital Audio) 141 | PowerMac3,5 142 | Power Macintosh G4 (Quick Silver) 143 | PowerMac3,6 144 | Power Macintosh G4 (Mirrored Drive Door) 145 | PowerMac4,1 146 | iMac G3 (Early/Summer 2001) 147 | PowerMac4,2 148 | iMac G4 (Flat Panel) 149 | PowerMac4,4 150 | eMac 151 | PowerMac4,5 152 | iMac G4 (17-inch Flat Panel) 153 | PowerMac5,1 154 | Power Macintosh G4 Cube 155 | PowerMac6,1 156 | iMac G4 (USB 2.0) 157 | PowerMac6,3 158 | iMac G4 (20-inch Flat Panel) 159 | PowerMac6,4 160 | eMac (USB 2.0, 2005) 161 | PowerMac7,2 162 | Power Macintosh G5 163 | PowerMac7,3 164 | Power Macintosh G5 165 | PowerMac8,1 166 | iMac G5 167 | PowerMac8,2 168 | iMac G5 (Ambient Light Sensor) 169 | PowerMac9,1 170 | Power Macintosh G5 (Late 2005) 171 | RackMac1,1 172 | Xserve G4 173 | RackMac1,2 174 | Xserve G4 (slot-loading, cluster node) 175 | RackMac3,1 176 | Xserve G5 177 | Xserve1,1 178 | Xserve (Intel Xeon) 179 | Xserve2,1 180 | Xserve (January 2008 quad-core) 181 | 182 | 183 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/SUStatus.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/SUStatus.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 11C74 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | finish_installation 11 | CFBundleIconFile 12 | Sparkle 13 | CFBundleIdentifier 14 | org.andymatuschak.sparkle.finish-installation 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DTCompiler 26 | com.apple.compilers.llvm.clang.1_0 27 | DTPlatformBuild 28 | 4D502 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 11C63 33 | DTSDKName 34 | macosx10.7 35 | DTXcode 36 | 0421 37 | DTXcodeBuild 38 | 4D502 39 | LSBackgroundOnly 40 | 1 41 | LSMinimumSystemVersion 42 | 10.4 43 | LSUIElement 44 | 1 45 | NSMainNibFile 46 | MainMenu 47 | NSPrincipalClass 48 | NSApplication 49 | 50 | 51 | -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/MacOS/finish_installation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/MacOS/finish_installation -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/SUStatus.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/SUStatus.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/Sparkle.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/Sparkle.icns -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/cs.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/cs.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/da.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/da.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/de.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/de.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/en.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/en.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/es.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/es.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/fr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/fr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/is.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/is.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/it.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/it.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ja.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ja.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/nl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/nl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pt_BR.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pt_BR.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pt_PT.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/pt_PT.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ro.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ro.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ru.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/ru.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/sl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/sl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/sv.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/sv.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/tr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/tr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/zh_CN.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/zh_CN.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/zh_TW.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/finish_installation.app/Contents/Resources/zh_TW.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/fr_CA.lproj: -------------------------------------------------------------------------------- 1 | fr.lproj -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt.lproj: -------------------------------------------------------------------------------- 1 | pt_BR.lproj -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Sparkle.framework/Versions/A/Sparkle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Sparkle.framework/Versions/A/Sparkle -------------------------------------------------------------------------------- /Sparkle.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /Tedium/AppDelegate+HelperTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+HelperTool.h 3 | // From ControlPlane 4 | // 5 | // Created by David Jennes on 05/09/11. 6 | // Modified for use with Tedium by Dustin Rue 7 | // Copyright 2011. All rights reserved. 8 | // 9 | 10 | #import "AppDelegate.h" 11 | #import "TediumHelpertoolCommon.h" 12 | 13 | @interface AppDelegate (HelperTool) 14 | 15 | - (NSInteger) helperToolPerformAction: (NSString *) action; 16 | - (NSInteger) helperToolPerformAction: (NSString *) action withParameter: (id) parameter; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Tedium/AppDelegate+HelperTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+HelperTool.h 3 | // From ControlPlane 4 | // 5 | // Created by David Jennes on 05/09/11. 6 | // Modified for use with Tedium by Dustin Rue 7 | // Copyright 2011. All rights reserved. 8 | // 9 | 10 | #import "AppDelegate+HelperTool.h" 11 | #import "TediumHelpertoolCommon.h" 12 | 13 | @interface AppDelegate (HelperTool_Private) 14 | 15 | - (OSStatus) helperToolActualPerform: (NSString *) action 16 | withParameter: (id) parameter 17 | response: (CFDictionaryRef *) response 18 | auth: (AuthorizationRef) auth; 19 | - (void) helperToolInit: (AuthorizationRef *) auth; 20 | - (OSStatus) helperToolFix: (BASFailCode) failCode withAuth: (AuthorizationRef) auth; 21 | - (void) helperToolAlert: (NSMutableDictionary *) parameters; 22 | 23 | @end 24 | 25 | @implementation AppDelegate (HelperTool) 26 | 27 | 28 | - (NSInteger) helperToolPerformAction: (NSString *) action { 29 | NSLog(@"called helper tool without a parameter"); 30 | return [self helperToolPerformAction:action withParameter:nil]; 31 | } 32 | 33 | - (NSInteger) helperToolPerformAction: (NSString *) action withParameter: (id) parameter { 34 | static int32_t versionCheck = 0; 35 | 36 | CFDictionaryRef response = NULL; 37 | AuthorizationRef auth = NULL; 38 | OSStatus error = noErr; 39 | NSLog(@"calling helper tool with %@", (NSString *)action); 40 | // initialize 41 | [self helperToolInit: &auth]; 42 | 43 | if (!versionCheck) { 44 | // start version check 45 | OSAtomicIncrement32(&versionCheck); 46 | 47 | // get version of helper tool 48 | NSLog(@"checking HelperTool version"); 49 | error = [self helperToolActualPerform: @kTediumHelperToolGetVersionCommand withParameter:nil response: &response auth: auth]; 50 | if (error) { 51 | NSLog(@"HelperTool version check failed"); 52 | OSAtomicDecrement32(&versionCheck); 53 | return NO; 54 | } 55 | 56 | // check version and update if needed 57 | NSNumber *version = [(__bridge_transfer NSDictionary *) response objectForKey: @kTediumHelperToolGetVersionResponse]; 58 | if ([version intValue] < kTediumHelperToolVersionNumber) 59 | [self helperToolFix: kBASFailNeedsUpdate withAuth: auth]; 60 | 61 | // finish version check 62 | OSAtomicIncrement32(&versionCheck); 63 | } 64 | 65 | // wait until version check is done 66 | while (versionCheck < 2) 67 | [NSThread sleepForTimeInterval: 1]; 68 | 69 | // perform actual action 70 | 71 | 72 | error = [self helperToolActualPerform: (NSString *) action withParameter: parameter response: &response auth: auth]; 73 | 74 | 75 | return (error); 76 | } 77 | 78 | - (OSStatus) helperToolActualPerform: (NSString *) action 79 | withParameter: (id) parameter 80 | response: (CFDictionaryRef *) response 81 | auth: (AuthorizationRef) auth { 82 | 83 | NSString *bundleID; 84 | NSDictionary *request; 85 | OSStatus error = 0; 86 | *response = NULL; 87 | 88 | // create request 89 | bundleID = [[NSBundle mainBundle] bundleIdentifier]; 90 | assert(bundleID != NULL); 91 | if (parameter) 92 | request = [NSDictionary dictionaryWithObjectsAndKeys: 93 | action, @kBASCommandKey, 94 | parameter, @"param", nil]; 95 | else 96 | request = [NSDictionary dictionaryWithObjectsAndKeys: action, @kBASCommandKey, nil]; 97 | assert(request != NULL); 98 | 99 | // Execute it. 100 | error = BASExecuteRequestInHelperTool(auth, 101 | kTediumHelperToolCommandSet, 102 | (__bridge_retained CFStringRef) bundleID, 103 | (__bridge_retained CFDictionaryRef) request, 104 | response); 105 | 106 | // If it failed, try to recover. 107 | if (error != noErr && error != userCanceledErr) { 108 | NSLog(@"HelperTool call failed, it probably needs to get installed"); 109 | BASFailCode failCode = BASDiagnoseFailure(auth, (__bridge_retained CFStringRef) bundleID); 110 | 111 | // try to fix 112 | error = [self helperToolFix: failCode withAuth: auth]; 113 | 114 | // If the fix went OK, retry the request. 115 | if (error == noErr) 116 | error = BASExecuteRequestInHelperTool(auth, 117 | kTediumHelperToolCommandSet, 118 | (__bridge_retained CFStringRef) bundleID, 119 | (__bridge_retained CFDictionaryRef) request, 120 | response); 121 | else { 122 | NSLog(@"fixing HelperTool failed"); 123 | } 124 | } 125 | NSLog(@"call to helper tool succeeded, helper tool returned: %d", error); 126 | 127 | // If all of the above went OK, it means that the IPC to the helper tool worked. We 128 | // now have to check the response dictionary to see if the command's execution within 129 | // the helper tool was successful. 130 | 131 | if (error == noErr) 132 | error = BASGetErrorFromResponse(*response); 133 | 134 | NSLog(@"command returned %d", error); 135 | return error; 136 | } 137 | 138 | - (void) helperToolInit: (AuthorizationRef *) auth { 139 | OSStatus err = 0; 140 | 141 | // Create the AuthorizationRef that we'll use through this application. We ignore 142 | // any error from this. A failure from AuthorizationCreate is very unusual, and if it 143 | // happens there's no way to recover; Authorization Services just won't work. 144 | 145 | err = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, auth); 146 | assert(err == noErr); 147 | assert((err == noErr) == (*auth != NULL)); 148 | 149 | // For each of our commands, check to see if a right specification exists and, if not, 150 | // create it. 151 | // 152 | // The last parameter is the name of a ".strings" file that contains the localised prompts 153 | // for any custom rights that we use. 154 | 155 | BASSetDefaultRules(*auth, 156 | kTediumHelperToolCommandSet, 157 | CFBundleGetIdentifier(CFBundleGetMainBundle()), 158 | CFSTR("HelperToolAuthorizationPrompts")); 159 | } 160 | 161 | - (OSStatus) helperToolFix: (BASFailCode) failCode withAuth: (AuthorizationRef) auth { 162 | NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; 163 | NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; 164 | OSStatus err = noErr; 165 | 166 | // At this point we tell the user that something has gone wrong and that we need 167 | // to authorize in order to fix it. Ideally we'd use failCode to describe the type of 168 | // error to the user. 169 | 170 | [self performSelectorOnMainThread: @selector(helperToolAlert:) withObject: parameters waitUntilDone: YES]; 171 | err = [[parameters objectForKey: @"result"] intValue]; 172 | 173 | // Try to fix things. 174 | if (err == NSAlertDefaultReturn) { 175 | 176 | err = BASFixFailure(auth, ((__bridge_retained CFStringRef) bundleID), CFSTR("TediumHelperInstallTool"), CFSTR("TediumHelperTool"), failCode); 177 | 178 | } else 179 | err = userCanceledErr; 180 | 181 | return err; 182 | } 183 | 184 | - (void) helperToolAlert: (NSMutableDictionary *) parameters { 185 | NSInteger t = NSRunAlertPanel(NSLocalizedString(@"Tedium Helper Needed", @"Fix helper tool"), 186 | NSLocalizedString(@"Tedium needs to install a helper app read and modify Time Machine settings", @"Fix helper tool"), 187 | NSLocalizedString(@"Install", @"Fix helper tool"), 188 | NSLocalizedString(@"Cancel", @"Fix helper tool"), 189 | NULL); 190 | 191 | [parameters setObject: [NSNumber numberWithInteger: t] forKey: @"result"]; 192 | } 193 | 194 | @end 195 | -------------------------------------------------------------------------------- /Tedium/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/16/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "BWQuincyManager.h" 12 | #import "NetworkBrowser.h" 13 | 14 | @class Destination; 15 | 16 | #define STATUS_BAR_LINGER 10 17 | 18 | @interface AppDelegate : NSObject { 19 | NSImage *menuBarImage; 20 | NSStatusItem *menuBarStatusItem; 21 | NSProcessInfo *processInfo; 22 | NSNotification *windowCloseNotification; 23 | NSDistributedNotificationCenter *notifications; 24 | NSTimer *sbHideTimer; 25 | 26 | 27 | 28 | IBOutlet NSWindow *prefsWindow; 29 | IBOutlet NSWindow *addNetworkShareSheet; 30 | IBOutlet NSWindow *bonjourBasedShareSheet; 31 | IBOutlet NSWindow *usernamePasswordSheet; 32 | IBOutlet NSWindow *aboutWindow; 33 | IBOutlet NSTableView *destinationsTableView; 34 | IBOutlet NSTableView *foundSharesTableView; 35 | IBOutlet NSButton *startAtLoginStatusForMenu; 36 | IBOutlet NSButton *hideMenuBarIconStatusForMenu; 37 | IBOutlet NSButton *localSnapshotsStatusForMenu; 38 | 39 | __unsafe_unretained NSMenu *destinationsSubMenu; 40 | } 41 | 42 | enum { 43 | kDestinationVolumeSetSuccessfully = 0, 44 | kDestinationVolumeDoesNotExist = 512, 45 | kDestinationVolumeUnreachable = 1280, 46 | kDestinationVolumeNotAvailable = 16384, 47 | kDestinationVolumeInvalidFormat = 52736, 48 | }; 49 | 50 | 51 | @property (assign) IBOutlet NSWindow *window; 52 | @property (strong) IBOutlet NSMenu *menuBarMenu; 53 | @property (readwrite, retain,nonatomic) NSString *currentDestination; 54 | @property (readwrite, retain) NSURL *currentDestinationAsNSURL; 55 | @property (readwrite, assign) NSWindow *activeSheet; 56 | @property (assign) NSString *destinationValueFromSheet; 57 | @property (retain) NSMutableArray *destinations; 58 | @property (retain) Destination *destination; 59 | @property (assign) BOOL hideMenuBarIconStatus; 60 | @property (assign) BOOL localSnapshotsStatus; 61 | @property (readwrite, retain) NetworkBrowser *networkBrowser; 62 | @property (readwrite, retain) NSMutableArray *foundDisks; 63 | @property (readwrite, retain) NSDictionary *selectedBonjourShare; 64 | @property (readwrite, retain) NSString *usernameFromSheet; 65 | @property (readwrite, retain) NSString *passwordFromSheet; 66 | @property (readonly, retain) NSString *versionString; 67 | @property (readwrite, retain) NSMutableDictionary *shareToBeAdded; 68 | 69 | @property (unsafe_unretained) IBOutlet NSButton *checkForUpdatesStatusForMenu; 70 | @property (unsafe_unretained) IBOutlet NSTextView *creditsFile; 71 | @property (unsafe_unretained) IBOutlet NSMenu *destinationsSubMenu; 72 | @property (weak) IBOutlet NSMenuItem *mobileBackupNowMenuItem; 73 | @property (weak) IBOutlet NSMenuItem *backupNowMenuItem; 74 | @property (weak) IBOutlet NSTextField *usernameFieldControl; 75 | 76 | 77 | - (NSImage *) prepareImageForMenubar:(NSString *)name; 78 | - (void) showInStatusBar:(id)sender; 79 | - (void) setMenuBarImage:(NSImage *)imageName; 80 | - (void) growlMessage:(NSString *)title message:(NSString *)message; 81 | - (NSDictionary *)parseDestination:(NSString *)destination; 82 | - (NSMutableArray *)getDestinationsForScripting; 83 | - (void) enableHideMenuBarIcon; 84 | - (void) disableHideMenuBarIcon; 85 | - (BOOL) willHideMenuBarIcon; 86 | - (void) populateDestinationsSubMenu; 87 | - (void) applyDestinationViaMenu:(id) sender; 88 | - (BOOL) isLocalSnapshotsEnabled; 89 | - (void) toggleMobileBackupMenuItem; 90 | - (void) toggleBackupMenuItem; 91 | - (void) saveSettings; 92 | - (void) addNewDestination:(NSDictionary *)newDestination; 93 | - (NSString *)cleanURL:(NSString *)urlToClean; 94 | 95 | 96 | - (IBAction)openPreferences:(id)sender; 97 | - (IBAction)addBonjourBasedNetworkShare:(id)sender; 98 | - (IBAction)addNetworkShare:(id)sender; 99 | - (IBAction)addAttachedExternalDrive:(id)sender; 100 | - (IBAction)closeSheetWithOK:(id)sender; 101 | - (IBAction)closeSheetWithCancel:(id)sender; 102 | - (IBAction)closePreferences:(id)sender; 103 | - (IBAction)applyNewDestination:(id)sender; 104 | - (IBAction)removeDestination:(id)sender; 105 | - (IBAction)editDestination:(id)sender; 106 | - (IBAction)toggleStartAtLoginAction:(id)sender; 107 | - (IBAction)openTediumGitHubIssues:(id)sender; 108 | - (IBAction)openDonationPage:(id)sender; 109 | - (IBAction)toggleHideMenuBarIcon:(id)sender; 110 | - (IBAction)toggleCheckForUpdates:(id)sender; 111 | - (IBAction)showAbout:(id)sender; 112 | - (IBAction)toggleMobileBackups:(id)sender; 113 | - (IBAction)doMobileBackupNow:(id)sender; 114 | - (IBAction)doBackupNow:(id)sender; 115 | 116 | // Login item stuff 117 | - (NSURL *)appPath; 118 | - (BOOL)willStartAtLogin:(NSURL *)appPath; 119 | - (void)startAtLogin; 120 | - (void)disableStartAtLogin; 121 | 122 | 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /Tedium/AppleScriptSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppleScriptSupport.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/27/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSApplication (AppleScript) 12 | - (NSNumber *)ready; 13 | - (NSArray *) destinations; 14 | - (void) setCurrentDestination:(NSString *)newDestination; 15 | @end 16 | -------------------------------------------------------------------------------- /Tedium/AppleScriptSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppleScriptSupport.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/27/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "AppleScriptSupport.h" 10 | #import "Destination.h" 11 | #import "AppDelegate.h" 12 | 13 | @implementation NSApplication (AppleScriptSupport) 14 | 15 | - (NSNumber *)ready { 16 | NSLog(@"derp"); 17 | return [NSNumber numberWithBool:YES]; 18 | } 19 | 20 | - (void) setCurrentDestination:(NSString *)newDestination 21 | { 22 | [[NSApp delegate] setCurrentDestination:newDestination]; 23 | } 24 | 25 | - (NSArray *) destinations { 26 | 27 | NSMutableArray *destinationsToReturn = [[NSMutableArray alloc] init]; 28 | 29 | for (NSDictionary *dict in [[NSApp delegate] getDestinationsForScripting]) { 30 | Destination *tmp = [[Destination alloc] init]; 31 | [tmp setDestinationVolumeName:[dict valueForKey:@"destinationVolumePath"]]; 32 | [destinationsToReturn addObject:tmp]; 33 | } 34 | 35 | return destinationsToReturn; 36 | 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Tedium/Destination.h: -------------------------------------------------------------------------------- 1 | // 2 | // Destination.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/27/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | // Heavily inspired by http://developer.apple.com/library/mac/#samplecode/SimpleScriptingObjects/Introduction/Intro.html 9 | // 10 | 11 | #import 12 | 13 | @interface Destination : NSObject { 14 | 15 | id container; 16 | NSString *containerProperty; 17 | 18 | } 19 | @property (retain) NSString *destinationVolumeName; 20 | @property (retain,readonly) id container; 21 | @property (retain,readonly) NSString *containerProperty; 22 | @property (copy) NSString *uniqueID; 23 | @property (copy) NSString *name; 24 | 25 | 26 | -(id)init; 27 | + (NSString *)makeId; 28 | - (NSScriptObjectSpecifier *)objectSpecifier; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Tedium/Destination.m: -------------------------------------------------------------------------------- 1 | // 2 | // Destination.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/27/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "Destination.h" 10 | #include 11 | #include 12 | 13 | @implementation Destination 14 | 15 | @synthesize destinationVolumeName; 16 | @synthesize container; 17 | @synthesize containerProperty; 18 | @synthesize uniqueID; 19 | @synthesize name; 20 | 21 | - (NSScriptObjectSpecifier *)objectSpecifier { 22 | return [[NSUniqueIDSpecifier alloc] 23 | initWithContainerClassDescription:(NSScriptClassDescription*) [self.container classDescription] 24 | containerSpecifier:[self.container objectSpecifier] 25 | key:self.containerProperty uniqueID:self.uniqueID]; 26 | 27 | } 28 | 29 | -(id) init { 30 | if ((self = [super init])) { 31 | static unsigned long gNameCounter = 1; 32 | self.uniqueID = [Destination makeId]; 33 | /* we use a global counter to generate unique names */ 34 | self.name = [NSString stringWithFormat:@"Untitled %d", gNameCounter++]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | 41 | + (NSString *)makeId { 42 | static unsigned long uniqueDestinationCounter; 43 | static pid_t myPID; /* unique id of our process */ 44 | static BOOL uniqueIdHasBeenInitialized = NO; /* our element id generator */ 45 | NSString *uniqueId; 46 | 47 | if (!uniqueIdHasBeenInitialized ) { 48 | myPID = getpid(); 49 | uniqueDestinationCounter = 1; 50 | uniqueIdHasBeenInitialized = YES; 51 | } 52 | 53 | uniqueId = [NSString stringWithFormat:@"SSO-%d-%d", myPID, uniqueDestinationCounter++]; 54 | 55 | return uniqueId; 56 | } 57 | 58 | 59 | 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Tedium/KeychainServices.h: -------------------------------------------------------------------------------- 1 | // 2 | // Originally AGKeychain.h 3 | // Based on code from "Core Mac OS X and Unix Programming" 4 | // by Mark Dalrymple and Aaron Hillegass 5 | // http://borkware.com/corebook/source-code 6 | // 7 | // Created by Adam Gerson on 3/6/05. 8 | // agerson@mac.com 9 | // 10 | // Updated 1/22/2012 by Dustin Rue silence compiler warnings 11 | // and work with Tedium 12 | // ruedu@dustinrue.com 13 | // 14 | 15 | #import 16 | 17 | 18 | @interface KeychainServices : NSObject { 19 | 20 | } 21 | + (NSString *)getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)item; 22 | + (BOOL)checkForExistanceOfKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *) address; 23 | + (BOOL)modifyKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withNewPassword:(NSString *)newPassword withAddress:(NSString *)address; 24 | + (BOOL)deleteKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *)address; 25 | + (BOOL)addKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withPassword:(NSString *)password withAddress:(NSString *)address; 26 | + (NSString *)getPasswordFromKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *)address; 27 | + (NSString *)getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)item; 28 | @end 29 | -------------------------------------------------------------------------------- /Tedium/KeychainServices.m: -------------------------------------------------------------------------------- 1 | // 2 | // Originally AGKeychain.h 3 | // Based on code from "Core Mac OS X and Unix Programming" 4 | // by Mark Dalrymple and Aaron Hillegass 5 | // http://borkware.com/corebook/source-code 6 | // 7 | // Created by Adam Gerson on 3/6/05. 8 | // agerson@mac.com 9 | // 10 | // Updated 1/22/2012 by Dustin Rue silence compiler warnings 11 | // and work with Tedium 12 | // ruedu@dustinrue.com 13 | // 14 | 15 | 16 | #import "KeychainServices.h" 17 | 18 | #import 19 | #import 20 | 21 | @implementation KeychainServices 22 | 23 | 24 | 25 | + (BOOL)checkForExistanceOfKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *) address 26 | { 27 | 28 | CFTypeRef results; 29 | OSErr result; 30 | 31 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 32 | 33 | [query setObject:(id)kSecClassInternetPassword forKey:kSecClass]; 34 | [query setObject:username forKey:kSecAttrAccount]; 35 | [query setObject:keychainItemKind forKey:kSecAttrDescription]; 36 | [query setObject:address forKey:kSecAttrServer]; 37 | [query setObject:keychainItemName forKey:kSecAttrLabel]; 38 | [query setObject:(id)kCFBooleanTrue forKey:kSecReturnAttributes]; 39 | [query setObject:[NSNumber numberWithInt:2] forKey:kSecMatchLimit]; 40 | 41 | result = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&results); 42 | 43 | NSDictionary *resultsAsDictionary = (__bridge_transfer NSDictionary*)results; 44 | 45 | if (result != noErr) { 46 | NSLog (@"failed with %d\n", result); 47 | } 48 | else { 49 | NSLog(@"found %lu items",[resultsAsDictionary count]); 50 | } 51 | 52 | return [resultsAsDictionary count]; 53 | } 54 | 55 | + (BOOL)deleteKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *)address 56 | { 57 | OSErr result; 58 | 59 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 60 | 61 | [query setObject:(id)kSecClassInternetPassword forKey:kSecClass]; 62 | [query setObject:username forKey:kSecAttrAccount]; 63 | [query setObject:keychainItemKind forKey:kSecAttrDescription]; 64 | [query setObject:address forKey:kSecAttrServer]; 65 | [query setObject:keychainItemName forKey:kSecAttrLabel]; 66 | [query setObject:(id)kCFBooleanTrue forKey:kSecReturnRef]; 67 | 68 | result = SecItemDelete((__bridge CFDictionaryRef)query); 69 | 70 | return !result; 71 | } 72 | 73 | + (BOOL)modifyKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withNewPassword:(NSString *)newPassword withAddress:(NSString *)address 74 | { 75 | OSErr result; 76 | 77 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 78 | 79 | [query setObject:(id)kSecClassInternetPassword forKey:kSecClass]; 80 | [query setObject:username forKey:kSecAttrAccount]; 81 | [query setObject:keychainItemKind forKey:kSecAttrDescription]; 82 | [query setObject:address forKey:kSecAttrServer]; 83 | [query setObject:keychainItemName forKey:kSecAttrLabel]; 84 | [query setObject:(id)kCFBooleanTrue forKey:kSecReturnRef]; 85 | 86 | NSMutableDictionary *updateAttributes = [NSMutableDictionary dictionaryWithDictionary:query]; 87 | 88 | [updateAttributes setObject:[newPassword dataUsingEncoding:NSUTF8StringEncoding] forKey:kSecValueData]; 89 | 90 | result = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes); 91 | 92 | if (result != noErr) { 93 | NSLog(@"Error modifying item: %d", (int)result); 94 | } 95 | 96 | 97 | return !result; 98 | } 99 | 100 | + (BOOL)addKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withPassword:(NSString *)password withAddress:(NSString *)address 101 | { 102 | OSErr result; 103 | 104 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 105 | 106 | [query setObject:(id)kSecClassInternetPassword forKey:kSecClass]; 107 | [query setObject:username forKey:kSecAttrAccount]; 108 | [query setObject:keychainItemKind forKey:kSecAttrDescription]; 109 | [query setObject:address forKey:kSecAttrServer]; 110 | [query setObject:keychainItemName forKey:kSecAttrLabel]; 111 | [query setObject:(id)kCFBooleanTrue forKey:kSecReturnRef]; 112 | [query setObject:[password dataUsingEncoding:NSUTF8StringEncoding] forKey:kSecValueData]; 113 | 114 | result = SecItemAdd((__bridge CFDictionaryRef)query,NULL); 115 | 116 | if (result != noErr) { 117 | NSLog(@"Error adding item: %d", (int)result); 118 | } 119 | 120 | return !result; 121 | } 122 | 123 | + (NSString *)getPasswordFromKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withAddress:(NSString *)address 124 | { 125 | CFTypeRef results; 126 | OSErr result; 127 | 128 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 129 | 130 | [query setObject:(id)kSecClassInternetPassword forKey:kSecClass]; 131 | [query setObject:username forKey:kSecAttrAccount]; 132 | [query setObject:keychainItemKind forKey:kSecAttrDescription]; 133 | [query setObject:address forKey:kSecAttrServer]; 134 | [query setObject:keychainItemName forKey:kSecAttrLabel]; 135 | [query setObject:(id)kCFBooleanTrue forKey:kSecReturnRef]; 136 | 137 | result = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&results); 138 | 139 | NSString *password = @""; 140 | if (result == noErr) { 141 | password = [self getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)results]; 142 | if(!password) { 143 | password = @""; 144 | } 145 | } 146 | return password; 147 | } 148 | 149 | + (NSString *)getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)item 150 | { 151 | UInt32 length; 152 | char *password; 153 | SecKeychainAttribute attributes[8]; 154 | SecKeychainAttributeList list; 155 | OSStatus status; 156 | 157 | attributes[0].tag = kSecAccountItemAttr; 158 | attributes[1].tag = kSecDescriptionItemAttr; 159 | attributes[2].tag = kSecLabelItemAttr; 160 | attributes[3].tag = kSecModDateItemAttr; 161 | 162 | list.count = 4; 163 | list.attr = attributes; 164 | 165 | status = SecKeychainItemCopyContent (item, NULL, &list, &length, 166 | (void **)&password); 167 | 168 | // use this version if you don't really want the password, 169 | // but just want to peek at the attributes 170 | //status = SecKeychainItemCopyContent (item, NULL, &list, NULL, NULL); 171 | 172 | // make it clear that this is the beginning of a new 173 | // keychain item 174 | if (status == noErr) { 175 | if (password != NULL) { 176 | 177 | // copy the password into a buffer so we can attach a 178 | // trailing zero byte in order to be able to print 179 | // it out with printf 180 | char passwordBuffer[1024]; 181 | 182 | if (length > 1023) { 183 | length = 1023; // save room for trailing \0 184 | } 185 | strncpy (passwordBuffer, password, length); 186 | 187 | passwordBuffer[length] = '\0'; 188 | //printf ("passwordBuffer = %s\n", passwordBuffer); 189 | return [NSString stringWithUTF8String:passwordBuffer]; 190 | } 191 | 192 | SecKeychainItemFreeContent (&list, password); 193 | 194 | } else { 195 | printf("Error = %d\n", (int)status); 196 | return @"Error getting password"; 197 | } 198 | return @"reached end of method and shouldn't have, yay you!"; 199 | } 200 | 201 | 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /Tedium/NSArray+Bonjour.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Bonjour.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (Bonjour) 12 | 13 | - (NSDictionary *) dictionary; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Tedium/NSArray+Bonjour.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Bonjour.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Bonjour.h" 10 | #import "NSString+Bonjour.h" 11 | 12 | @implementation NSArray (Bonjour) 13 | 14 | - (NSDictionary *) dictionary { 15 | NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithCapacity:[self count]]; 16 | for (NSString *item in self) { 17 | [tmp setValue:[item shareValue] forKey:[item shareType]]; 18 | } 19 | 20 | return tmp; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Tedium/NSDictionary+Bonjour.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Bonjour.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (Bonjour) 12 | 13 | - (BOOL) isTimeMachineShare; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Tedium/NSDictionary+Bonjour.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Bonjour.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+Bonjour.h" 10 | 11 | @implementation NSDictionary (Bonjour) 12 | 13 | - (BOOL) isTimeMachineShare { 14 | return ([self objectForKey:@"adVF"] && [self objectForKey:@"adVN"]); 15 | } 16 | @end 17 | -------------------------------------------------------------------------------- /Tedium/NSString+Bonjour.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ADisk.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (ADisk) 12 | 13 | - (NSString *) shareType; 14 | - (NSString *) shareValue; 15 | - (NSString *) withTrailingPeriodRemoved; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tedium/NSString+Bonjour.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ADisk.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/26/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "NSString+Bonjour.h" 10 | 11 | @implementation NSString (ADisk) 12 | 13 | 14 | - (NSString *) shareType { 15 | NSArray *exploded = [self componentsSeparatedByString:@"="]; 16 | 17 | if ([exploded count] != 2) 18 | return @""; 19 | 20 | return [exploded objectAtIndex:0]; 21 | } 22 | 23 | - (NSString *) shareValue { 24 | NSArray *exploded = [self componentsSeparatedByString:@"="]; 25 | 26 | if ([exploded count] != 2) 27 | return @""; 28 | 29 | return [exploded objectAtIndex:1]; 30 | } 31 | 32 | - (NSString *) withTrailingPeriodRemoved { 33 | if ([[self substringFromIndex:[self length] - 1] isEqualToString:@"."]) { 34 | return [self substringToIndex:[self length] - 1]; 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Tedium/NetworkBrowser.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkBrowser.h 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/9/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol DRNetworkBrowserDelegate ; 12 | 13 | - (void) foundDisksDidChange; 14 | 15 | @end 16 | 17 | @interface NetworkBrowser : NSObject 18 | 19 | @property (readwrite, retain) NSNetServiceBrowser *netBrowser; 20 | @property (readwrite, retain) NSMutableArray *foundServers; 21 | @property (nonatomic, assign) id delegate; 22 | 23 | - (void) start; 24 | - (void) stop; 25 | 26 | @end 27 | 28 | 29 | -------------------------------------------------------------------------------- /Tedium/NetworkBrowser.m: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkBrowser.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 2/9/12. 6 | // Copyright (c) 2012 Dustin Rue. All rights reserved. 7 | // 8 | 9 | #import "NetworkBrowser.h" 10 | 11 | 12 | @implementation NetworkBrowser 13 | 14 | @synthesize netBrowser; 15 | @synthesize foundServers; 16 | @synthesize delegate; 17 | 18 | - (NetworkBrowser *)init { 19 | if ((self = [super init])) { 20 | [self setNetBrowser:[[NSNetServiceBrowser alloc] init]]; 21 | [[self netBrowser] setDelegate:self]; 22 | 23 | [self setFoundServers:[[NSMutableArray alloc] init]]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | - (void) start { 30 | [[self netBrowser] searchForServicesOfType:@"_adisk._tcp." inDomain:@"local."]; 31 | } 32 | 33 | - (void) stop { 34 | [[self netBrowser] stop]; 35 | } 36 | 37 | - (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing { 38 | 39 | [[self foundServers] addObject:netService]; 40 | 41 | if (!moreServicesComing) { 42 | [netService setDelegate:self]; 43 | [netService resolveWithTimeout:1.5]; 44 | } 45 | } 46 | 47 | 48 | - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing { 49 | 50 | [[self foundServers] removeObject:aNetService]; 51 | 52 | if (!moreComing) { 53 | if ([[self delegate] respondsToSelector:@selector(foundDisksDidChange)]) 54 | [[self delegate] foundDisksDidChange]; 55 | } 56 | 57 | } 58 | 59 | - (void)netServiceDidResolveAddress:(NSNetService *)sender { 60 | if ([[self delegate] respondsToSelector:@selector(foundDisksDidChange)]) 61 | [[self delegate] foundDisksDidChange]; 62 | } 63 | 64 | 65 | - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict { 66 | NSLog(@"failed to resolve found services!"); 67 | } 68 | 69 | 70 | - (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)aNetServiceBrowser { 71 | if ([[self delegate] respondsToSelector:@selector(foundDisksDidChange)]) 72 | [[self delegate] foundDisksDidChange]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Tedium/PopButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopButton.h 3 | // Taken from ControlPlane 4 | // 5 | // Created by David Symonds on 26/04/07. 6 | // 7 | 8 | 9 | @interface PopButton : NSButton { 10 | NSMenu *menu; 11 | } 12 | 13 | - (NSMenu *)menu; 14 | - (void)setMenu:(NSMenu *)theMenu; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Tedium/PopButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // PopButton.m 3 | // Taken from ControlPlane 4 | // 5 | // Created by David Symonds on 26/04/07. 6 | // 7 | 8 | #import "PopButton.h" 9 | 10 | 11 | @implementation PopButton 12 | 13 | - (id)init 14 | { 15 | if (!(self = [super init])) 16 | return nil; 17 | 18 | menu = nil; 19 | 20 | return self; 21 | } 22 | 23 | - (void)dealloc 24 | { 25 | } 26 | 27 | - (NSMenu *)menu 28 | { 29 | return menu; 30 | } 31 | 32 | - (void)setMenu:(NSMenu *)theMenu 33 | { 34 | 35 | menu = theMenu; 36 | } 37 | 38 | - (void)mouseDown:(NSEvent *)theEvent 39 | { 40 | if (![self isEnabled] || !menu) 41 | return; 42 | 43 | NSRect rect = [self bounds]; 44 | NSPoint pt = NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height + 5); 45 | NSEvent *event = [NSEvent mouseEventWithType:[theEvent type] 46 | location:[self convertPoint:pt toView:nil] 47 | modifierFlags:[theEvent modifierFlags] 48 | timestamp:[theEvent timestamp] 49 | windowNumber:[theEvent windowNumber] 50 | context:[theEvent context] 51 | eventNumber:[theEvent eventNumber] 52 | clickCount:[theEvent clickCount] 53 | pressure:[theEvent pressure]]; 54 | 55 | [self highlight:YES]; 56 | [NSMenu popUpContextMenu:menu withEvent:event forView:self]; 57 | [self highlight:NO]; 58 | } 59 | 60 | - (void)mouseUp:(NSEvent *)theEvent 61 | { 62 | if ([self isEnabled]) 63 | [self highlight:NO]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Tedium/QuincyKit-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDocumentTypes 8 | 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | de.buzzworks.QuincyMacDemo 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | NSServices 32 | 33 | UTExportedTypeDeclarations 34 | 35 | UTImportedTypeDeclarations 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Tedium/QuincyKit-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Tedium' target in the 'Tedium' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /Tedium/Tedium-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | awesomeclock.icns 11 | CFBundleIdentifier 12 | com.dustinrue.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 7 25 | LSApplicationCategoryType 26 | public.app-category.utilities 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | LSUIElement 30 | 31 | NSAppleScriptEnabled 32 | 33 | NSHumanReadableCopyright 34 | Copyright © 2012. All rights reserved. 35 | NSMainNibFile 36 | MainMenu 37 | NSPrincipalClass 38 | NSApplication 39 | OSAScriptingDefinition 40 | Tedium.sdef 41 | SUFeedURL 42 | http://www.tediumapp.com/appcast.xml 43 | SUPublicDSAKeyFile 44 | dsa_pub.pem 45 | TediumCrashReportURL 46 | http://crashreport.tediumapp.com/crash_v200.php 47 | TediumDonationsURL 48 | http://www.tediumapp.com/support-tedium 49 | TediumGithubIssuesURL 50 | https://github.com/dustinrue/Tedium/issues 51 | 52 | 53 | -------------------------------------------------------------------------------- /Tedium/Tedium-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Tedium' target in the 'Tedium' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /Tedium/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf230 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\b\fs24 \cf0 Engineering: 8 | \b0 \ 9 | Dustin Rue\ 10 | \ 11 | 12 | \b Human Interface Design: 13 | \b0 \ 14 | Dustin Rue\ 15 | \ 16 | 17 | \b Testing: 18 | \b0 \ 19 | Dustin Rue\ 20 | \ 21 | 22 | \b Icon: 23 | \b0 \ 24 | Jed Limke\ 25 | \ 26 | 27 | \b With special thanks to: 28 | \b0 \ 29 | Mom\ 30 | The Internet\ 31 | } -------------------------------------------------------------------------------- /Tedium/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Tedium/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Tedium 4 | // 5 | // Created by Dustin Rue on 1/16/12. 6 | // Copyright (c) 2012. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /Utilities/Tedium-Template.dmg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dustinrue/Tedium/2eb2af6e1dc977e46b0f1d49af0028c619a06a46/Utilities/Tedium-Template.dmg -------------------------------------------------------------------------------- /Utilities/make_debug_image.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # make_disk_image.sh 4 | # run this from the root project dir! 5 | # ControlPlane 6 | # 7 | # Created by David Symonds on 17/02/07. 8 | # Modified by Dustin Rue on 7/28/2011. 9 | # Modified by Dustin Rue on 9/03/2011. 10 | # 11 | 12 | # Get version number 13 | VERSION=`cat Info.plist | grep -A 1 'CFBundleShortVersionString' | \ 14 | tail -1 | sed "s/[<>]/|/g" | cut -d\| -f3` 15 | 16 | APPNAME=ControlPlane 17 | IMG=$APPNAME-$VERSION-DEBUG 18 | IMGTMP=Utilities/ControlPlane-Template 19 | CONFIGURATION=Debug 20 | APP=build/$CONFIGURATION/$APPNAME.app 21 | 22 | if [ "$1" == "release" ]; then 23 | cd Utilities 24 | ./update-oui.sh 25 | ./update-usb-data.sh 26 | cd .. 27 | fi 28 | 29 | xcodebuild -configuration "$CONFIGURATION" clean build 30 | if [ ! -d "$APP" ]; then 31 | echo "Something failed in the build process!" 32 | exit 1 33 | fi 34 | 35 | # Create an initial disk image (32 megs) 36 | if [ -f "$IMG" ]; then rm "$IMG"; fi 37 | hdiutil convert $IMGTMP.dmg -format UDSP -o $IMG || exit 1 38 | #hdiutil create -size 32m -fs HFS+ -volname "$APPNAME-$VERSION" "$IMG" || exit 1 39 | 40 | # Mount the disk image 41 | echo "attaching disk" 42 | #hdiutil attach "$IMG.sparseimage" || exit 1 43 | 44 | # Obtain device information 45 | DEVS=$(hdiutil attach "$IMG.sparseimage" | grep HFS) 46 | DEV=$(echo $DEVS | cut -d ' ' -f 1) 47 | ROOT=$(echo $DEVS | cut -d ' ' -f 3) 48 | echo "done" 49 | 50 | # Copy files 51 | echo "$ROOT/$APPNAME" 52 | rm -rf $ROOT/$APPNAME.app/Contents 53 | mkdir $ROOT/$APPNAME.app/Contents 54 | cp -R $APP/Contents/* $ROOT/$APPNAME.app/Contents/ 55 | VERSION=`defaults read $ROOT/$APPNAME.app/Contents/Info.plist CFBundleVersion` 56 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist CFBundleVersion $VERSION-DEBUG 57 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist CFBundleShortVersionString $VERSION-DEBUG 58 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist SUFeedURL "http://localhost/" 59 | if [ -f "$ICON" ]; then 60 | cp $ICON $ROOT/.VolumeIcon.icns 61 | /Developer/Tools/SetFile -a C $ROOT 62 | fi 63 | 64 | # Unmount the disk image 65 | hdiutil detach $DEV || exit 1 66 | 67 | # Convert the disk image to read-only 68 | #TMP="tmp-${IMG}" 69 | #mv "$IMG" "$TMP" 70 | hdiutil convert "$IMG.sparseimage" -format UDBZ -o "$IMG" 71 | rm "$IMG.sparseimage" 72 | 73 | 74 | # sign the file for Sparkle 75 | # run a helper script exposing location of private key for Sparkle updates 76 | if [ "$1" == "release" ]; then 77 | . cp_pm_env.sh 78 | ls -l "$IMG" 79 | ruby "$SIGNING_SCRIPT" "$IMG" "$PRIVATE_KEY" 80 | mv "$IMG" "$IMGDEST" 81 | fi 82 | 83 | if [ "$1" == "open" ]; then 84 | open "$IMG" 85 | fi 86 | -------------------------------------------------------------------------------- /Utilities/make_disk_image.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # make_disk_image.sh 4 | # run this from the root project dir! 5 | # ControlPlane 6 | # 7 | # Created by David Symonds on 17/02/07. 8 | # Modified by Dustin Rue on 7/28/2011. 9 | # Modified by Dustin Rue on 9/03/2011. 10 | # 11 | 12 | # Get version number 13 | VERSION=`cat Tedium/Tedium-Info.plist | grep -A 1 'CFBundleShortVersionString' | \ 14 | tail -1 | sed "s/[<>]/|/g" | cut -d\| -f3` 15 | 16 | APPNAME=Tedium 17 | IMG=$APPNAME-$VERSION 18 | IMGTMP=Utilities/Tedium-Template 19 | CONFIGURATION=Release 20 | APP=build/$CONFIGURATION/$APPNAME.app 21 | 22 | if [ "$1" == "release" ]; then 23 | cd Utilities 24 | cd .. 25 | fi 26 | 27 | xcodebuild -configuration "$CONFIGURATION" clean build 28 | if [ ! -d "$APP" ]; then 29 | echo "Something failed in the build process!" 30 | exit 1 31 | fi 32 | 33 | # Create an initial disk image (32 megs) 34 | if [ -f "$IMG" ]; then rm "$IMG"; fi 35 | hdiutil convert $IMGTMP.dmg -format UDSP -o $IMG || exit 1 36 | #hdiutil create -size 32m -fs HFS+ -volname "$APPNAME-$VERSION" "$IMG" || exit 1 37 | 38 | # Mount the disk image 39 | echo "attaching disk" 40 | #hdiutil attach "$IMG.sparseimage" || exit 1 41 | 42 | # Obtain device information 43 | DEVS=$(hdiutil attach "$IMG.sparseimage" | grep HFS) 44 | DEV=$(echo $DEVS | cut -d ' ' -f 1) 45 | ROOT=$(echo $DEVS | cut -d ' ' -f 3) 46 | echo "done" 47 | 48 | # Copy files 49 | echo "$ROOT/$APPNAME" 50 | rm -rf $ROOT/$APPNAME.app/Contents 51 | mkdir $ROOT/$APPNAME.app/Contents 52 | cp -R $APP/Contents/* $ROOT/$APPNAME.app/Contents/ 53 | if [ -f "$ICON" ]; then 54 | cp $ICON $ROOT/.VolumeIcon.icns 55 | /Developer/Tools/SetFile -a C $ROOT 56 | fi 57 | 58 | # Unmount the disk image 59 | hdiutil detach $DEV || exit 1 60 | 61 | # Convert the disk image to read-only 62 | #TMP="tmp-${IMG}" 63 | #mv "$IMG" "$TMP" 64 | hdiutil convert "$IMG.sparseimage" -format UDBZ -o "$IMG" 65 | rm "$IMG.sparseimage" 66 | 67 | 68 | # sign the file for Sparkle 69 | # run a helper script exposing location of private key for Sparkle updates 70 | if [ "$1" == "release" ]; then 71 | . tedium_pm_env.sh 72 | ls -l "$IMG.dmg" 73 | ruby "$SIGNING_SCRIPT" "$IMG.dmg" "$PRIVATE_KEY" 74 | mv "$IMG.dmg" "$IMGDEST" 75 | fi 76 | 77 | if [ "$1" == "open" ]; then 78 | open "$IMG.dmg" 79 | fi 80 | -------------------------------------------------------------------------------- /Utilities/make_nightly_image.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # make_disk_image.sh 4 | # run this from the root project dir! 5 | # ControlPlane 6 | # 7 | # Created by David Symonds on 17/02/07. 8 | # Modified by Dustin Rue on 7/28/2011. 9 | # Modified by Dustin Rue on 9/03/2011. 10 | # 11 | 12 | # Get version number 13 | VERSION=`cat Info.plist | grep -A 1 'CFBundleShortVersionString' | \ 14 | tail -1 | sed "s/[<>]/|/g" | cut -d\| -f3` 15 | 16 | APPNAME=ControlPlane 17 | theDate=`date "+%m-%d-%Y"` 18 | IMG=$APPNAME-NIGHTLY-${theDate} 19 | IMGTMP=Utilities/ControlPlane-Template 20 | CONFIGURATION=Debug 21 | APP=build/$CONFIGURATION/$APPNAME.app 22 | 23 | if [ "$1" == "release" ]; then 24 | cd Utilities 25 | ./update-oui.sh 26 | ./update-usb-data.sh 27 | cd .. 28 | fi 29 | 30 | xcodebuild -configuration "$CONFIGURATION" clean build 31 | if [ ! -d "$APP" ]; then 32 | echo "Something failed in the build process!" 33 | exit 1 34 | fi 35 | 36 | # Create an initial disk image (32 megs) 37 | if [ -f "$IMG" ]; then rm "$IMG"; fi 38 | hdiutil convert $IMGTMP.dmg -format UDSP -o $IMG || exit 1 39 | #hdiutil create -size 32m -fs HFS+ -volname "$APPNAME-$VERSION" "$IMG" || exit 1 40 | 41 | # Mount the disk image 42 | echo "attaching disk" 43 | #hdiutil attach "$IMG.sparseimage" || exit 1 44 | 45 | # Obtain device information 46 | DEVS=$(hdiutil attach "$IMG.sparseimage" | grep HFS) 47 | DEV=$(echo $DEVS | cut -d ' ' -f 1) 48 | ROOT=$(echo $DEVS | cut -d ' ' -f 3) 49 | echo "done" 50 | 51 | # Copy files 52 | echo "$ROOT/$APPNAME" 53 | rm -rf $ROOT/$APPNAME.app/Contents 54 | mkdir $ROOT/$APPNAME.app/Contents 55 | cp -R $APP/Contents/* $ROOT/$APPNAME.app/Contents/ 56 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist CFBundleVersion ${theDate} 57 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist CFBundleShortVersionString ${theDate} 58 | defaults write $ROOT/$APPNAME.app/Contents/Info.plist SUFeedURL "http://localhost/" 59 | 60 | if [ -f "$ICON" ]; then 61 | cp $ICON $ROOT/.VolumeIcon.icns 62 | /Developer/Tools/SetFile -a C $ROOT 63 | fi 64 | 65 | # Unmount the disk image 66 | hdiutil detach $DEV || exit 1 67 | 68 | # Convert the disk image to read-only 69 | #TMP="tmp-${IMG}" 70 | #mv "$IMG" "$TMP" 71 | hdiutil convert "$IMG.sparseimage" -format UDBZ -o "$IMG" 72 | rm "$IMG.sparseimage" 73 | 74 | 75 | # sign the file for Sparkle 76 | # run a helper script exposing location of private key for Sparkle updates 77 | if [ "$1" == "release" ]; then 78 | . cp_pm_env.sh 79 | ls -l "$IMG" 80 | ruby "$SIGNING_SCRIPT" "$IMG" "$PRIVATE_KEY" 81 | mv "$IMG" "$IMGDEST" 82 | fi 83 | 84 | if [ "$1" == "open" ]; then 85 | open "$IMG" 86 | fi 87 | -------------------------------------------------------------------------------- /Utilities/remove_helper_tool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # a simple utility for removing the Tedium helper app 4 | # *must* be run as root 5 | 6 | launchctl unload -F /Library/LaunchDaemons/com.dustinrue.Tedium.plist 7 | rm /Library/LaunchDaemons/com.dustinrue.Tedium.plist 8 | rm /Library/PrivilegedHelperTools/com.dustinrue.Tedium 9 | --------------------------------------------------------------------------------