// // MGMController.m // CocoaShare // // Created by Mr. Gecko on 1/15/11. // Copyright (c) 2015 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/ // #import "MGMController.h" #import "MGMPathSubscriber.h" #import "MGMLoginItems.h" #import "MGMAddons.h" #import "MGMMenuItem.h" #import "RegexKitLite.h" #import #import #import #import NSString * const MGMCopyright = @"Copyright (c) 2015 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/ All rights reserved."; NSString * const MGMVersion = @"MGMVersion"; NSString * const MGMLaunchCount = @"MGMLaunchCount"; NSString * const MGMDisplay = @"MGMDisplay"; NSString * const MGMStartup = @"MGMStartup"; NSString * const MGMUploadName = @"MGMUploadName"; NSString * const MGMHistoryCount = @"MGMHistoryCount"; NSString * const MGMGrowlErrors = @"MGMGrowlErrors"; NSString * const MGMUploadLimit = @"MGMUploadLimit"; NSString * const MGMHistoryPlist = @"history.plist"; NSString * const MGMHURL = @"url"; NSString * const MGMHInfo = @"info"; NSString * const MGMHDate = @"date"; NSString * const MGMESound = @"MGME%dSound"; NSString * const MGMEPath = @"MGME%dPath"; NSString * const MGMEDelete = @"MGME%dDelete"; NSString * const MGMEGrowl = @"MGME%dGrowl"; const int MGMEUploadingAutomatic = 0; const int MGMEUploadedAutomatic = 1; const int MGMEUploading = 2; const int MGMEUploaded = 3; NSString * const MGMEventNotification = @"MGMEventNotification"; NSString * const MGMEvent = @"event"; NSString * const MGMEventPath = @"path"; NSString * const MGMEventURL = @"URL"; NSString * const MGMFiltersPlist = @"filters.plist"; NSString * const MGMFID = @"id"; NSString * const MGMFPath = @"path"; NSString * const MGMFFilter = @"filter"; NSString * const MGMResizePlist = @"resize.plist"; NSString * const MGMRID = @"id"; NSString * const MGMRWidth = @"width"; NSString * const MGMRHeight = @"height"; NSString * const MGMRScale = @"scale"; NSString * const MGMRFilters = @"filters"; NSString * const MGMRNetworks = @"networks"; NSString * const MGMRIPPrefix = @"IPPrefix"; NSString * const MGMPluginFolder = @"PlugIns"; NSString * const MGMCurrentPlugIn = @"MGMCurrentPlugIn"; NSString * const MGMMUThemesFolder = @"Multi Upload Themes"; NSString * const MGMCurrentMUTheme = @"MGMCurrentMUTheme"; NSString * const MGMKCType = @"application password"; NSString * const MGMKCName = @"CocoaShare"; NSString * const MGMUPath = @"path"; NSString * const MGMUAutomatic = @"automatic"; NSString * const MGMUMultiUpload = @"multiUpload"; NSString * const MGMNSStringPboardType = @"NSStringPboardType"; NSString * const MGMNSPasteboardTypeString = @"public.utf8-plain-text"; OSStatus frontAppChanged(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) { ProcessSerialNumber thisProcess; GetCurrentProcess(&thisProcess); ProcessSerialNumber newProcess; GetFrontProcess(&newProcess); Boolean same; SameProcess(&newProcess, &thisProcess, &same); if (!same) [(MGMController *)userData setFrontProcess:&newProcess]; return (CallNextEventHandler(nextHandler, theEvent)); } static MGMController *MGMSharedController; NSString * const MGMPrimaryInterface = @"PrimaryInterface"; NSString * const MGMAddresses = @"Addresses"; NSString * const MGMIPv4Info = @"State:/Network/Interface/%@/IPv4"; NSString * const MGMIPv6Info = @"State:/Network/Interface/%@/IPv6"; NSString * const MGMIPv4State = @"State:/Network/Global/IPv4"; NSString * const MGMIPv6State = @"State:/Network/Global/IPv6"; NSString * const MGMAirPortInfo = @"State:/Network/Interface/%@/AirPort"; static void systemNotification(SCDynamicStoreRef store, NSArray *changedKeys, void *info) { for (int i=0; i<[changedKeys count]; ++i) { NSString *key = [changedKeys objectAtIndex:i]; if ([key isEqual:MGMIPv4State]) { NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key); [(MGMController *)info ipv4Changed:value]; [value release]; } else if ([key isEqual:MGMIPv6State]) { NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key); [(MGMController *)info ipv6Changed:value]; [value release]; } else if ([key hasSuffix:@"AirPort"]) { NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key); [(MGMController *)info airportChanged:value]; [value release]; } } } @implementation MGMController + (id)sharedController { if (MGMSharedController==nil) { MGMSharedController = [MGMController new]; } return MGMSharedController; } - (id)init { if (MGMSharedController!=nil) { if ((self = [super init])) [self release]; self = MGMSharedController; } else if ((self = [super init])) { MGMSharedController = self; } return self; } - (void)awakeFromNib { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setup) name:MGMGRDoneNotification object:nil]; [MGMReporter sharedReporter]; } - (void)setup { autoreleaseDrain = [[NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(drainAutoreleasePool) userInfo:nil repeats:YES] retain]; [GrowlApplicationBridge setGrowlDelegate:nil]; NSString *AirPortBSDName = nil; CFArrayRef interfaces = SCNetworkInterfaceCopyAll(); for (int i=0; i0) [self addMenu]; preferences = [MGMPreferences new]; [preferences addPreferencesPaneClassName:@"MGMGeneralPane"]; [preferences addPreferencesPaneClassName:@"MGMAccountPane"]; [preferences addPreferencesPaneClassName:@"MGMAutoUploadPane"]; [preferences addPreferencesPaneClassName:@"MGMResizePane"]; [preferences addPreferencesPaneClassName:@"MGMEventsPane"]; EventTypeSpec eventType; eventType.eventClass = kEventClassApplication; eventType.eventKind = kEventAppFrontSwitched; EventHandlerUPP handlerUPP = NewEventHandlerUPP(frontAppChanged); InstallApplicationEventHandler(handlerUPP, 1, &eventType, self, NULL); if ([defaults integerForKey:MGMLaunchCount]==2) [preferences showPreferences]; about = [MGMAbout new]; uploads = [NSMutableArray new]; if ([defaults objectForKey:MGMVersion]==nil || [[defaults objectForKey:MGMVersion] doubleValue]<=0.3) { for (int i=0; i<[filters count]; i++) { if ([[filters objectAtIndex:i] objectForKey:MGMFID]==nil) { CFUUIDRef uuid = CFUUIDCreate(NULL); NSString *uuidString = [(NSString *)CFUUIDCreateString(NULL, uuid) autorelease]; CFRelease(uuid); NSDictionary *filter = [[filters objectAtIndex:i] mutableCopy]; [filter setValue:uuidString forKey:MGMFID]; [filters replaceObjectAtIndex:i withObject:filter]; [filter release]; } } [self saveFilters]; } [defaults setObject:[[MGMSystemInfo info] applicationVersion] forKey:MGMVersion]; [self loadMUThemes]; [self loadPlugIns]; } - (void)dealloc { [autoreleaseDrain invalidate]; [autoreleaseDrain release]; if (store!=NULL) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kCFRunLoopDefaultMode); CFRelease(store); } [IPv4Addresses release]; [IPv6Addresses release]; [lastAirPortState release]; [connectionManager release]; [preferences release]; [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; [statusItem release]; [menuItem release]; [history release]; [filters release]; [saveLock release]; [filterWatcher release]; [accountPlugIns release]; [plugIns release]; [uploads release]; [super dealloc]; } - (void)registerDefaults { NSMutableDictionary *defaults = [NSMutableDictionary dictionary]; [defaults setObject:[NSNumber numberWithInt:1] forKey:MGMLaunchCount]; [defaults setObject:[NSNumber numberWithInt:([[MGMSystemInfo info] isUIElement] ? 2 : 0)] forKey:MGMDisplay]; [defaults setObject:[NSNumber numberWithBool:[[MGMLoginItems items] thisApplicationExists]] forKey:MGMStartup]; [defaults setObject:[NSNumber numberWithInt:0] forKey:MGMUploadName]; [defaults setObject:[NSNumber numberWithInt:5] forKey:MGMHistoryCount]; [defaults setObject:[NSNumber numberWithInt:5] forKey:MGMUploadLimit]; [defaults setObject:[NSNumber numberWithInt:2] forKey:[NSString stringWithFormat:MGMEDelete, MGMEUploadedAutomatic]]; [defaults setObject:[[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:MGMMUThemesFolder] stringByAppendingPathComponent:@"White Background"] forKey:MGMCurrentMUTheme]; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; } - (void)drainAutoreleasePool { NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:CFAbsoluteTimeGetCurrent() windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [[NSApplication sharedApplication] postEvent:event atStart:NO]; } - (void)ipv4Changed:(NSDictionary *)theInfo { NSDictionary *info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv4Info, [theInfo objectForKey:MGMPrimaryInterface]]); [IPv4Addresses autorelease]; IPv4Addresses = [[info objectForKey:MGMAddresses] retain]; [info release]; } - (void)ipv6Changed:(NSDictionary *)theInfo { NSDictionary *info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv6Info, [theInfo objectForKey:MGMPrimaryInterface]]); [IPv6Addresses autorelease]; IPv6Addresses = [[info objectForKey:MGMAddresses] retain]; [info release]; } - (void)airportChanged:(NSDictionary *)theInfo { [lastAirPortState autorelease]; lastAirPortState = [theInfo retain]; } - (MGMURLConnectionManager *)connectionManager { return connectionManager; } - (MGMPreferences *)preferences { return preferences; } - (void)loadPlugIns { NSFileManager *manager = [NSFileManager defaultManager]; [accountPlugIns release]; accountPlugIns = [NSMutableArray new]; [plugIns release]; plugIns = [NSMutableArray new]; NSArray *checkPaths = [NSArray arrayWithObjects:[[NSBundle mainBundle] builtInPlugInsPath], [[MGMUser applicationSupportPath] stringByAppendingPathComponent:MGMPluginFolder], nil]; for (int i=0; i<[checkPaths count]; i++) { NSArray *plugInsFolder = [manager contentsOfDirectoryAtPath:[checkPaths objectAtIndex:i]]; for (int p=0; p<[plugInsFolder count]; p++) { NSString *path = [[[checkPaths objectAtIndex:i] stringByAppendingPathComponent:[plugInsFolder objectAtIndex:p]] stringByResolvingSymlinksInPath]; NSBundle *bundle = [NSBundle bundleWithPath:path]; if (bundle!=nil) { Class plugInClass = [bundle principalClass]; id plugIn = [[[plugInClass alloc] init] autorelease]; if (plugIn!=nil && [plugIn respondsToSelector:@selector(isAccountPlugIn)] && [plugIn isAccountPlugIn]) [accountPlugIns addObject:plugIn]; else if (plugIn!=nil) [plugIns addObject:plugIn]; } } } NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *currentPlugInName = [defaults objectForKey:MGMCurrentPlugIn]; BOOL foundCurrentPlugIn = NO; for (int i=0; i<[accountPlugIns count]; i++) { if ([NSStringFromClass([[accountPlugIns objectAtIndex:i] class]) isEqual:currentPlugInName]) { currentPlugIn = [accountPlugIns objectAtIndex:i]; currentPlugInIndex = i; if ([currentPlugIn respondsToSelector:@selector(setCurrentPlugIn:)]) [currentPlugIn setCurrentPlugIn:YES]; foundCurrentPlugIn = YES; break; } } if (!foundCurrentPlugIn && [accountPlugIns count]>0) [self setCurrentPlugIn:[accountPlugIns objectAtIndex:0]]; } - (NSArray *)accountPlugIns { return accountPlugIns; } - (NSArray *)plugIns { return plugIns; } - (void)setCurrentPlugIn:(id)thePlugIn { int plugInIndex = [accountPlugIns indexOfObject:thePlugIn]; if (plugInIndex>=0) { [self removePassword]; if ([currentPlugIn respondsToSelector:@selector(setCurrentPlugIn:)]) [currentPlugIn setCurrentPlugIn:NO]; currentPlugIn = thePlugIn; currentPlugInIndex = plugInIndex; [[NSUserDefaults standardUserDefaults] setObject:NSStringFromClass([currentPlugIn class]) forKey:MGMCurrentPlugIn]; if ([currentPlugIn respondsToSelector:@selector(setCurrentPlugIn:)]) [currentPlugIn setCurrentPlugIn:YES]; } } - (id)currentPlugIn { return currentPlugIn; } - (int)currentPlugInIndex { return currentPlugInIndex; } - (void)loadMUThemes { NSFileManager *manager = [NSFileManager defaultManager]; [MUThemes release]; MUThemes = [NSMutableArray new]; NSArray *checkPaths = [NSArray arrayWithObjects:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:MGMMUThemesFolder], [[MGMUser applicationSupportPath] stringByAppendingPathComponent:MGMMUThemesFolder], nil]; for (int i=0; i<[checkPaths count]; i++) { NSArray *MUThemesFolder = [manager contentsOfDirectoryAtPath:[checkPaths objectAtIndex:i]]; for (int p=0; p<[MUThemesFolder count]; p++) { NSString *path = [[[checkPaths objectAtIndex:i] stringByAppendingPathComponent:[MUThemesFolder objectAtIndex:p]] stringByResolvingSymlinksInPath]; [MUThemes addObject:path]; } } NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *currentMUThemePath = [defaults objectForKey:MGMCurrentMUTheme]; BOOL foundCurrentMUTheme = NO; for (int i=0; i<[MUThemes count]; i++) { if ([[MUThemes objectAtIndex:i] isEqual:currentMUThemePath]) { currentMUThemeIndex = i; foundCurrentMUTheme = YES; break; } } if (!foundCurrentMUTheme && [MUThemes count]>0) [self setCurrentMUTheme:[MUThemes objectAtIndex:0]]; } - (NSArray *)MUThemes { return MUThemes; } - (void)setCurrentMUTheme:(NSString *)theMUTheme { int MUThemeIndex = [MUThemes indexOfObject:theMUTheme]; if (MUThemeIndex>=0) { currentMUThemeIndex = MUThemeIndex; [[NSUserDefaults standardUserDefaults] setObject:theMUTheme forKey:MGMCurrentMUTheme]; } } - (int)currentMUThemeIndex { return currentMUThemeIndex; } - (NSString *)currentMUTheme { return [MUThemes objectAtIndex:currentMUThemeIndex]; } - (void)setFrontProcess:(ProcessSerialNumber *)theProcess { frontProcess = *theProcess; /*CFStringRef name; CopyProcessName(theProcess, &name); if (name!=NULL) { NSLog(@"%@ became front", (NSString *)name); CFRelease(name); }*/ } - (void)becomeFront:(NSWindow *)theWindow { if (theWindow!=nil) { windowCount++; if ([[MGMSystemInfo info] isUIElement]) [theWindow setLevel:NSFloatingWindowLevel]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frontWindowClosed:) name:NSWindowWillCloseNotification object:theWindow]; } [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; } - (void)resignFront { SetFrontProcess(&frontProcess); } - (void)frontWindowClosed:(NSNotification *)theNotification { [[NSNotificationCenter defaultCenter] removeObserver:self name:[theNotification name] object:[theNotification object]]; windowCount--; if (windowCount==0) [self resignFront]; } - (void)addMenu { if (statusItem==nil) { menuItem = [[MGMMenuItem alloc] initWithFrame:NSZeroRect]; [menuItem setDelegate:self]; NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; if ([osxMode isEqualTo:@"Dark"]) { [menuItem setImage:[NSImage imageNamed:@"menuiconselected"]]; [menuItem setAlternateImage:[NSImage imageNamed:@"menuicon"]]; } else { [menuItem setImage:[NSImage imageNamed:@"menuicon"]]; [menuItem setAlternateImage:[NSImage imageNamed:@"menuiconselected"]]; } [menuItem setToolTip:@"CocoaShare"]; statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain]; [statusItem setView:menuItem]; } } - (void)removeMenu { if (statusItem!=nil) { [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; [statusItem release]; statusItem = nil; [menuItem release]; menuItem = nil; } } - (void)setDockHidden:(BOOL)isHidden { NSFileManager *manager = [NSFileManager defaultManager]; NSBundle *bundle = [NSBundle mainBundle]; NSString *path = [[bundle bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"]; if ([manager isWritableFileAtPath:path]) { NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:path]; [infoDict setObject:[NSNumber numberWithBool:isHidden] forKey:@"LSUIElement"]; [infoDict writeToFile:path atomically:NO]; NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate]; [manager setAttributes:attributes ofItemAtPath:[bundle bundlePath]]; if (isHidden) { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Restart Required" localized]]; [alert setInformativeText:[@"Inorder to hide the dock, you must restart CocoaShare. Do you want to restart CocoaShare now?" localized]]; [alert addButtonWithTitle:[@"Yes" localized]]; [alert addButtonWithTitle:[@"No" localized]]; int result = [alert runModal]; if (result==1000) { //Took from Sparkle. NSString *pathToRelaunch = [[NSBundle mainBundle] bundlePath]; NSString *relaunchPath = [[[NSBundle bundleWithIdentifier:@"org.andymatuschak.Sparkle"] resourcePath] stringByAppendingPathComponent:@"relaunch"]; [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], nil]]; [[NSApplication sharedApplication] terminate:self]; } } } else { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Unable to change dock" localized]]; [alert setInformativeText:[NSString stringWithFormat:[@"CocoaShare is unable to %@ the dock due to permissions. To fix this issue, right click on CocoaShare and choose Get Info to make CocoaShare writable." localized], (isHidden ? [@"hide" localized] : [@"unhide" localized])]]; [alert runModal]; } if (!isHidden) { ProcessSerialNumber psn = {0, kCurrentProcess}; TransformProcessType(&psn, kProcessTransformToForegroundApplication); [self resignFront]; [self performSelector:@selector(becomeFront:) withObject:[preferences preferencesWindow] afterDelay:0.0]; } } - (void)menuClicked:(id)sender { [statusItem popUpStatusItemMenu:mainMenu]; } - (void)menuDraggingEntered:(id)sender { [menuItem setImage:[NSImage imageNamed:@"menuicondrag"]]; } - (void)menuDraggingExited:(id)sender { NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; if ([osxMode isEqualTo:@"Dark"]) { [menuItem setImage:[NSImage imageNamed:@"menuiconselected"]]; } else { [menuItem setImage:[NSImage imageNamed:@"menuicon"]]; } } - (void)menu:(id)sender droppedFiles:(NSArray *)files { NSFileManager *manager = [NSFileManager defaultManager]; for (int i=0; i<[files count]; i++) { BOOL directory = NO; if ([manager fileExistsAtPath:[files objectAtIndex:i] isDirectory:&directory]) { if (directory) { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Upload Error" localized]]; [alert setInformativeText:[@"Uploading of directories is impossible." localized]]; [alert runModal]; continue; } [self addPathToUploads:[files objectAtIndex:i] automaticFilter:nil multiUpload:([files count]==1 ? 0 : (i==0 ? 1 : (i==[files count]-1 ? 3 : 2)))]; } } } - (NSMutableArray *)history { return history; } - (void)updateMenu { int splitterIndex = 0; for (int i=0; i<[mainMenu numberOfItems]; i++) { if ([[mainMenu itemAtIndex:i] isSeparatorItem]) { splitterIndex = i; break; } [mainMenu removeItemAtIndex:i]; i--; } if ([history count]>0) { for (int i=0; i<[history count]; i++) { NSDictionary *historyItem = [history objectAtIndex:i]; NSMenuItem *item = [[NSMenuItem new] autorelease]; NSDateFormatter *formatter = [[NSDateFormatter new] autorelease]; [formatter setDateFormat:[@"MMMM d, yyyy h:mm:ss a" localized]]; NSString *date = [formatter stringFromDate:[historyItem objectForKey:MGMHDate]]; if (date!=nil) [item setTitle:date]; else [item setTitle:[NSString stringWithFormat:@"%@", [historyItem objectForKey:MGMHDate]]]; [item setRepresentedObject:[historyItem objectForKey:MGMHURL]]; [item setTarget:self]; [item setAction:@selector(copyHistoryItem:)]; [mainMenu insertItem:item atIndex:splitterIndex]; } } else { NSMenuItem *item = [[NSMenuItem new] autorelease]; [item setTitle:[@"No Upload History" localized]]; [item setEnabled:NO]; [mainMenu insertItem:item atIndex:splitterIndex]; } } - (IBAction)copyHistoryItem:(id)sender { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; [pboard declareTypes:[NSArray arrayWithObjects:MGMNSStringPboardType, MGMNSPasteboardTypeString, nil] owner:nil]; [pboard setString:[sender representedObject] forType:MGMNSStringPboardType]; [pboard setString:[sender representedObject] forType:MGMNSPasteboardTypeString]; } - (void)addURLToHistory:(NSURL *)theURL { [history addObject:[NSDictionary dictionaryWithObjectsAndKeys:[theURL absoluteString], MGMHURL, [NSDate date], MGMHDate, nil]]; int maxHistoryItems = [[NSUserDefaults standardUserDefaults] integerForKey:MGMHistoryCount]; int itemsToDelete = [history count]-maxHistoryItems; if (itemsToDelete>0) { for (int i=0; i0) { for (int i=0; i0) [[items objectAtIndex:0] remove]; } - (void)setPassword:(NSString *)thePassword { NSArray *items = [MGMKeychain items:MGMKCType withName:MGMKCName service:MGMKCName account:MGMKCName]; if ([items count]>0) { [[items objectAtIndex:0] setString:thePassword]; } else { [MGMKeychain addItem:MGMKCType withName:MGMKCName service:MGMKCName account:MGMKCName password:thePassword]; } } - (NSString *)password { NSArray *items = [MGMKeychain items:MGMKCType withName:MGMKCName service:MGMKCName account:MGMKCName]; if ([items count]>0) return [[items objectAtIndex:0] string]; return nil; } - (void)processEvent:(int)theEvent path:(NSString *)thePath { [self processEvent:theEvent path:thePath url:nil]; } - (void)processEvent:(int)theEvent path:(NSString *)thePath url:(NSURL *)theURL { NSMutableDictionary *eventInfo = [NSMutableDictionary dictionary]; [eventInfo setObject:[NSNumber numberWithInt:theEvent] forKey:MGMEvent]; [eventInfo setObject:thePath forKey:MGMEventPath]; if (theURL!=nil) [eventInfo setObject:theURL forKey:MGMEventURL]; [[NSNotificationCenter defaultCenter] postNotificationName:MGMEventNotification object:self userInfo:eventInfo]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSFileManager *manager = [NSFileManager defaultManager]; NSString *soundPath = [defaults objectForKey:[NSString stringWithFormat:MGMESound, theEvent]]; if (soundPath!=nil && [manager fileExistsAtPath:soundPath]) { NSSound *sound = [[NSSound alloc] initWithContentsOfFile:soundPath byReference:YES]; [sound setDelegate:self]; [sound play]; } NSString *path = [[defaults objectForKey:[NSString stringWithFormat:MGMEPath, theEvent]] stringByExpandingTildeInPath]; if ([manager fileExistsAtPath:path]) [manager moveItemAtPath:thePath toPath:path]; int deleteFile = [[defaults objectForKey:[NSString stringWithFormat:MGMEDelete, theEvent]] intValue]; if (deleteFile!=0 && [manager fileExistsAtPath:thePath]) { if (deleteFile==1) { [manager removeItemAtPath:thePath]; } else if (deleteFile==2) { NSString *trash = [@"~/.Trash" stringByExpandingTildeInPath]; NSInteger tag; [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[thePath stringByDeletingLastPathComponent] destination:trash files:[NSArray arrayWithObject:[thePath lastPathComponent]] tag:&tag]; if (tag!=0) NSLog(@"Error Deleting: %ld", (long)tag); } } BOOL growl = [[defaults objectForKey:[NSString stringWithFormat:MGMEGrowl, theEvent]] boolValue]; if (growl) { NSString *title = nil; NSString *description = nil; NSString *notificationName = nil; if (theEvent==MGMEUploading) { title = [@"Uploading File" localized]; description = [NSString stringWithFormat:[@"Uploading %@" localized], [[thePath lastPathComponent] stringByDeletingPathExtension]]; notificationName = @"UploadingFile"; } else if (theEvent==MGMEUploadingAutomatic) { title = [@"Automatically Uploading File" localized]; description = [NSString stringWithFormat:[@"Uploading %@" localized], [[thePath lastPathComponent] stringByDeletingPathExtension]]; notificationName = @"UploadingFileAutomatically"; } else if (theEvent==MGMEUploaded) { title = [@"Uploaded File" localized]; description = [NSString stringWithFormat:[@"Uploaded %@ to %@" localized], [[thePath lastPathComponent] stringByDeletingPathExtension], theURL]; notificationName = @"UploadedFile"; } else if (theEvent==MGMEUploadedAutomatic) { title = [@"Automatically Uploaded File" localized]; description = [NSString stringWithFormat:[@"Uploaded %@ to %@" localized], [[thePath lastPathComponent] stringByDeletingPathExtension], theURL]; notificationName = @"UploadedFileAutomatically"; } [GrowlApplicationBridge notifyWithTitle:title description:description notificationName:notificationName iconData:[[[NSApplication sharedApplication] applicationIconImage] TIFFRepresentation] priority:0 isSticky:NO clickContext:nil]; } } - (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finishedPlaying { if (finishedPlaying) [sound release]; } - (NSMutableArray *)uploads { return uploads; } - (NSDictionary *)uploadForPath:(NSString *)thePath { for (int i=0; i<[uploads count]; i++) { if ([[[uploads objectAtIndex:i] objectForKey:MGMUPath] isEqual:thePath]) return [uploads objectAtIndex:i]; } return nil; } - (void)addPathToUploads:(NSString *)thePath automaticFilter:(NSString *)theFilter { [self addPathToUploads:thePath automaticFilter:theFilter multiUpload:0]; } /* 0 - Not a upload queue with multiple uploads. 1 - First upload in the queue. 2 - An upload in the queue. 3 - Last upload in the queue. 4 - The multi upload page. */ - (void)addPathToUploads:(NSString *)thePath automaticFilter:(NSString *)theFilter multiUpload:(int)multiUploadState { if ([self uploadForPath:thePath]==nil) { if ([currentPlugIn respondsToSelector:@selector(allowedExtensions)]) { if (![[currentPlugIn allowedExtensions] containsObject:[[thePath pathExtension] lowercaseString]]) { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Upload Error" localized]]; [alert setInformativeText:[@"The current PlugIn does not support this file format." localized]]; [alert runModal]; return; } } [self resizeIfNeeded:thePath filterID:theFilter]; [uploads addObject:[NSDictionary dictionaryWithObjectsAndKeys:thePath, MGMUPath, [NSNumber numberWithBool:(theFilter!=nil)], MGMUAutomatic, [NSNumber numberWithInt:multiUploadState], MGMUMultiUpload, nil]]; if ([uploads count]==1) [self processNextUpload]; } } - (void)processNextUpload { if ([uploads count]>0) { if (currentPlugIn==nil) { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Upload Error" localized]]; [alert setInformativeText:[@"No PlugIns found. You must have at least 1 PlugIn to upload a file." localized]]; [alert runModal]; [uploads removeAllObjects]; return; } else if (![currentPlugIn respondsToSelector:@selector(sendFileAtPath:withName:)] && ![currentPlugIn respondsToSelector:@selector(sendFileAtPath:withName:multiUpload:)]) { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Upload Error" localized]]; [alert setInformativeText:[@"The current PlugIn doesn't support uploading." localized]]; [alert runModal]; [uploads removeAllObjects]; return; } NSFileManager *manager = [NSFileManager defaultManager]; NSDictionary *upload = [uploads objectAtIndex:0]; int multiUpload = [[upload objectForKey:MGMUMultiUpload] intValue]; if (multiUpload==1) { [multiUploadLinks release]; multiUploadLinks = [NSMutableArray new]; } if (![manager fileExistsAtPath:[upload objectForKey:MGMUPath]]) { [uploads removeObject:upload]; [self processNextUpload]; return; } [menuItem setImage:[NSImage imageNamed:@"menuiconupload"]]; [self processEvent:([[upload objectForKey:MGMUAutomatic] boolValue] ? MGMEUploadingAutomatic : MGMEUploading) path:[upload objectForKey:MGMUPath]]; int uploadNameType = [[[NSUserDefaults standardUserDefaults] objectForKey:MGMUploadName] intValue]; NSString *randomizedName = [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] MD5]; NSString *name = [[upload objectForKey:MGMUPath] lastPathComponent]; if ((uploadNameType==0 && [[upload objectForKey:MGMUAutomatic] boolValue]) || uploadNameType==1) name = [randomizedName stringByAppendingPathExtension:[name pathExtension]]; if ([currentPlugIn respondsToSelector:@selector(sendFileAtPath:withName:multiUpload:)]) { [currentPlugIn sendFileAtPath:[upload objectForKey:MGMUPath] withName:name multiUpload:multiUpload]; } else { [currentPlugIn sendFileAtPath:[upload objectForKey:MGMUPath] withName:name]; } } else { NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; if ([osxMode isEqualTo:@"Dark"]) { [menuItem setImage:[NSImage imageNamed:@"menuiconselected"]]; } else { [menuItem setImage:[NSImage imageNamed:@"menuicon"]]; } } } - (void)upload:(NSString *)thePath receivedError:(NSError *)theError { NSDictionary *upload = [self uploadForPath:thePath]; if (upload!=nil) { NSLog(@"Error: %@", theError); if ([[NSUserDefaults standardUserDefaults] boolForKey:MGMGrowlErrors]) { [GrowlApplicationBridge notifyWithTitle:[@"Unable to upload" localized] description:[NSString stringWithFormat:@"%@: %@", [[upload objectForKey:MGMUPath] lastPathComponent], [theError localizedDescription]] notificationName:@"UploadError" iconData:[[[NSApplication sharedApplication] applicationIconImage] TIFFRepresentation] priority:1 isSticky:NO clickContext:nil]; } else { NSAlert *alert = [[NSAlert new] autorelease]; [alert setMessageText:[@"Upload Error" localized]]; [alert setInformativeText:[NSString stringWithFormat:[@"Unable to upload %@: %@" localized], [[upload objectForKey:MGMUPath] lastPathComponent], [theError localizedDescription]]]; [alert runModal]; } [uploads removeObject:upload]; [self processNextUpload]; } } - (void)uploadFinished:(NSString *)thePath url:(NSURL *)theURL { NSDictionary *upload = [self uploadForPath:thePath]; if (upload!=nil) { int multiUpload = [[upload objectForKey:MGMUMultiUpload] intValue]; [self processEvent:([[upload objectForKey:MGMUAutomatic] boolValue] ? MGMEUploadedAutomatic : MGMEUploaded) path:[upload objectForKey:MGMUPath] url:theURL]; //No need to bother the clip board when there will end up being a multi upload url. if (multiUpload==0 || multiUpload==4) { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; [pboard declareTypes:[NSArray arrayWithObjects:MGMNSStringPboardType, MGMNSPasteboardTypeString, nil] owner:nil]; [pboard setString:[theURL absoluteString] forType:MGMNSStringPboardType]; [pboard setString:[theURL absoluteString] forType:MGMNSPasteboardTypeString]; } [self addURLToHistory:theURL]; if (multiUpload>=1 && multiUpload!=4) { [multiUploadLinks addObject:theURL]; } if (multiUpload==3) { if ([currentPlugIn respondsToSelector:@selector(createMultiUploadPage)]) { [currentPlugIn createMultiUploadPage]; } else { NSString *randomizedName = [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] MD5]; NSString *htmlPath = [NSString stringWithFormat:@"/tmp/CocoaShare_%@.htm", randomizedName]; NSString *imageHTML = [NSString stringWithContentsOfFile:[[self currentMUTheme] stringByAppendingPathComponent:@"image.html"] encoding:NSUTF8StringEncoding error:nil]; if (imageHTML==nil) { [uploads removeObject:upload]; [self processNextUpload]; return; } NSString *fileHTML = [NSString stringWithContentsOfFile:[[self currentMUTheme] stringByAppendingPathComponent:@"file.html"] encoding:NSUTF8StringEncoding error:nil]; [[NSFileManager defaultManager] createFileAtPath:htmlPath contents:nil attributes:nil]; NSFileHandle *multiUploadHtml = [NSFileHandle fileHandleForWritingAtPath:htmlPath]; [multiUploadHtml writeData:[NSData dataWithContentsOfFile:[[self currentMUTheme] stringByAppendingPathComponent:@"header.html"]]]; NSArray *imageExtensions = [NSArray arrayWithObjects:@"jpg", @"jpeg", @"png", @"bmp", @"gif", nil]; for (int i=0; i<[multiUploadLinks count]; i++) { NSURL *link = [multiUploadLinks objectAtIndex:i]; NSString *linkString = [link absoluteString]; if ([imageExtensions containsObject:[[link pathExtension] lowercaseString]]) { [multiUploadHtml writeData:[[imageHTML replace:@"{url}" with:linkString] dataUsingEncoding:NSUTF8StringEncoding]]; } else { [multiUploadHtml writeData:[[fileHTML replace:@"{url}" with:linkString] dataUsingEncoding:NSUTF8StringEncoding]]; } } [multiUploadHtml writeData:[NSData dataWithContentsOfFile:[[self currentMUTheme] stringByAppendingPathComponent:@"footer.html"]]]; [multiUploadHtml closeFile]; [self addPathToUploads:htmlPath automaticFilter:nil multiUpload:4]; } } if (multiUpload==4) { [[NSFileManager defaultManager] removeItemAtPath:[upload objectForKey:MGMUPath]]; } [uploads removeObject:upload]; [self processNextUpload]; } } - (void)multiUploadPageCreated:(NSURL *)theURL { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; [pboard declareTypes:[NSArray arrayWithObjects:MGMNSStringPboardType, MGMNSPasteboardTypeString, nil] owner:nil]; [pboard setString:[theURL absoluteString] forType:MGMNSStringPboardType]; [pboard setString:[theURL absoluteString] forType:MGMNSPasteboardTypeString]; [self addURLToHistory:theURL]; } @end