KerberosController.m [plain text]
/*
* KerberosController.m
*
* $Header$
*
* Copyright 2004 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#import "KerberosController.h"
#import "KerberosErrorAlert.h"
#import "KerberosCredential.h"
#import "Utilities.h"
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <IOKit/IOMessage.h>
#import <Kerberos/KerberosLoginPrivate.h>
@implementation KerberosController
#pragma mark -
// ---------------------------------------------------------------------------
- (id) init
{
if ((self = [super init])) {
cacheCollection = [[KerberosCacheCollection sharedCacheCollection] retain];
if (!cacheCollection) {
[self release];
return NULL;
}
minuteTimer = NULL;
preferencesController = NULL;
ticketListController = NULL;
realmsEditorController = NULL;
ticketsKerberosIconImage = NULL;
warningKerberosIconImage = NULL;
noTicketsKerberosIconImage = NULL;
kerberosAppIconImage = NULL;
}
return self;
}
// ---------------------------------------------------------------------------
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: self];
if (cacheCollection ) { [cacheCollection release]; }
if (minuteTimer ) { [minuteTimer release]; }
if (preferencesController ) { [preferencesController release]; }
if (ticketListController ) { [ticketListController release]; }
if (ticketsKerberosIconImage ) { [ticketsKerberosIconImage release]; }
if (warningKerberosIconImage ) { [warningKerberosIconImage release]; }
if (noTicketsKerberosIconImage) { [noTicketsKerberosIconImage release]; }
if (kerberosAppIconImage ) { [kerberosAppIconImage release]; }
[super dealloc];
}
#pragma mark --- Actions --
// ---------------------------------------------------------------------------
- (IBAction) getTickets: (id) sender
{
KLStatus err = [KerberosPrincipal getTickets];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosGetTicketsAction];
}
}
// ---------------------------------------------------------------------------
- (IBAction) changePasswordForSelectedCache: (id) sender
{
if ([self haveTicketListWindow]) {
[ticketListController changePasswordForSelectedCache: self];
}
}
// ---------------------------------------------------------------------------
- (IBAction) changePasswordForActiveUser: (id) sender
{
KerberosCache *cache = [cacheCollection defaultCache];
if (cache) {
KLStatus err = [[cache principal] changePassword];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosChangePasswordAction];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) destroyTicketsForSelectedCache: (id) sender
{
if ([self haveTicketListWindow]) {
[ticketListController destroyTicketsForSelectedCache: self];
}
}
// ---------------------------------------------------------------------------
- (IBAction) destroyTicketsForActiveUser: (id) sender
{
KerberosCache *cache = [cacheCollection defaultCache];
if (cache) {
KLStatus err = [[cache principal] destroyTickets];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosDestroyTicketsAction];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) renewTicketsForSelectedCache: (id) sender
{
if ([self haveTicketListWindow]) {
[ticketListController renewTicketsForSelectedCache: self];
}
}
// ---------------------------------------------------------------------------
- (IBAction) renewTicketsForActiveUser: (id) sender
{
KerberosCache *cache = [cacheCollection defaultCache];
if (cache) {
KLStatus err = [[cache principal] renewTickets];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosRenewTicketsAction];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) validateTicketsForSelectedCache: (id) sender
{
if ([self haveTicketListWindow]) {
[ticketListController validateTicketsForSelectedCache: self];
}
}
// ---------------------------------------------------------------------------
- (IBAction) validateTicketsForActiveUser: (id) sender
{
KerberosCache *cache = [cacheCollection defaultCache];
if (cache) {
KLStatus err = [[cache principal] validateTickets];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosValidateTicketsAction];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) changeActiveUser: (id) sender
{
if ([sender isMemberOfClass: [NSMenuItem class]]) {
NSMenuItem *item = sender;
NSMenu *menu = [item menu];
int offset = 0;
if (menu == dockMenu) {
offset += [dockMenu indexOfItem: dockSeparatorItem] + 2; // include header item
}
if ([cacheCollection numberOfCaches] > 0) {
KerberosCache *cache = [cacheCollection cacheAtIndex: ([menu indexOfItem: item] - offset)];
if (![cache isDefault]) {
KLStatus err = [[cache principal] setDefault];
if (!err) {
[cacheCollection update];
} else if (err != klUserCanceledErr && err != KIM_USER_CANCELED_ERR) {
[KerberosErrorAlert alertForError: err
action: KerberosChangeActiveUserAction];
}
}
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) showTicketInfo: (id) sender
{
if ([self haveTicketListWindow]) {
[ticketListController showTicketInfo: self];
}
}
// ---------------------------------------------------------------------------
- (IBAction) showPreferences: (id) sender
{
if (!preferencesController) {
preferencesController = [[PreferencesController alloc] init];
}
[preferencesController showWindow: self];
}
// ---------------------------------------------------------------------------
- (IBAction) showAboutBox: (id) sender
{
[aboutWindow center];
[aboutWindow makeKeyAndOrderFront: self];
}
// ---------------------------------------------------------------------------
- (IBAction) showTicketList: (id) sender
{
if (!ticketListController) {
ticketListController = [[TicketListController alloc] init];
}
if (ticketListController) {
[ticketListController showWindow: self];
[self menuNeedsUpdate: ticketsMenu];
}
}
// ---------------------------------------------------------------------------
- (IBAction) editRealms: (id) sender
{
if (!realmsEditorController) {
realmsEditorController = [[RealmsEditorController alloc] init];
}
[realmsEditorController showWindow: self];
}
#pragma mark --- Delegate Methods --
// ---------------------------------------------------------------------------
- (void) awakeFromNib
{
dprintf ("Entering awakeFromNib...");
// Make sure we are always prompted with the dialog even if we have a controlling terminal
__KLSetPromptMechanism (klPromptMechanism_GUI);
// Fill in the version field in the about dialog
NSString *name = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleName"];
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"KfMDisplayVersion"];
NSString *copyright = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"KfMDisplayCopyright"];
[aboutVersionTextField setStringValue: [NSString stringWithFormat: @" (name != NULL) ? name : @"", (version != NULL) ? version : @""]];
[aboutCopyrightTextField setStringValue: (copyright != NULL) ? copyright : @""];
// Set up timers
minuteTimer = [[NSTimer scheduledTimerWithTimeInterval: 30 // controls the minute counter in the dock
target: self
selector: @selector(minuteTimer:)
userInfo: NULL
repeats: YES] retain];
// Enable and disable menus in these lists manually
// since it's easier to manually enable and disable the active user list
[ticketsMenu setAutoenablesItems: NO];
[activeUserMenu setAutoenablesItems: NO];
[dockMenu setAutoenablesItems: NO];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (preferencesDidChange:)
name: PreferencesDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (cacheCollectionDidChange:)
name: CacheCollectionDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (ticketListDidChange:)
name: TicketListDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (cacheSelectionDidChange:)
name: CacheSelectionDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (ticketSelectionDidChange:)
name: TicketSelectionDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (windowWillClose:)
name: NSWindowWillCloseNotification
object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: @selector (wakeFromSleep:)
name: NSWorkspaceDidWakeNotification
object: nil];
// disable items before ticket list opens to set up default state
[self ticketListDidChange: NULL];
// Open ticket list, if needed (will send notification if opened)
if (([[KerberosPreferences sharedPreferences] launchAction] == LaunchActionAlwaysOpenTicketWindow) ||
(([[KerberosPreferences sharedPreferences] launchAction] == LaunchActionRememberOpenTicketWindow) &&
([[KerberosPreferences sharedPreferences] ticketWindowLastOpen]))) {
[self showTicketList: self];
}
// Set up default cache state
[self cacheCollectionDidChange: NULL];
}
// ---------------------------------------------------------------------------
- (NSMenu *) applicationDockMenu: (NSApplication *) sender
{
return dockMenu;
}
// ---------------------------------------------------------------------------
- (void) menuNeedsUpdate: (NSMenu *) menu
{
if (menu == ticketsMenu) {
//dprintf ("Kerberos Controller tickets menu updating...");
BOOL ticketListWindowHasSelectedCache = [self ticketListWindowHasSelectedCache];
[renewTicketsMenuItem setEnabled: ticketListWindowHasSelectedCache];
[validateTicketsMenuItem setEnabled: [self ticketListWindowSelectedCacheNeedsValidation]];
[destroyTicketsMenuItem setEnabled: ticketListWindowHasSelectedCache];
[changePasswordMenuItem setEnabled: ticketListWindowHasSelectedCache];
[showTicketInfoMenuItem setEnabled: [self ticketListWindowHasSelectedCredential]];
} else if (menu == activeUserMenu) {
//dprintf ("Kerberos Controller active user menu updating...");
[Utilities synchronizeCacheMenu: menu
popup: NO
staticPrefixItemsCount: 0
headerItem: NO
checkDefaultCache: YES
defaultCacheIndex: NULL
selector: @selector (changeActiveUser:)
sender: self];
} else if (menu == dockMenu) {
//dprintf ("Kerberos Controller dock menu updating...");
BOOL haveDefaultCache = [self haveDefaultCache];
[dockRenewTicketsMenuItem setEnabled: haveDefaultCache];
[dockValidateTicketsMenuItem setEnabled: [self defaultCacheNeedsValidation]];
[dockDestroyTicketsMenuItem setEnabled: haveDefaultCache];
[dockChangePasswordMenuItem setEnabled: haveDefaultCache];
[Utilities synchronizeCacheMenu: menu
popup: NO
staticPrefixItemsCount: [dockMenu indexOfItem: dockSeparatorItem] + 1
headerItem: YES
checkDefaultCache: YES
defaultCacheIndex: NULL
selector: @selector (changeActiveUser:)
sender: self];
}
//dprintf ("Kerberos Controller done updating menu...");
}
#pragma mark --- Notifications --
// ---------------------------------------------------------------------------
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
{
// this handles showing the Ticket List whenever the Dock icon is clicked
// unlike applicationDidBecomeActive, this gets called even when Kerberos.app
// is already active and doesn't when cmd-tabbing to Kerberos.app
[self showTicketList: self];
return flag;
}
// ---------------------------------------------------------------------------
- (void) applicationWillTerminate: (NSNotification *) notification
{
// Put the dock icon back
NSImage *applicationIconImage = [NSImage imageNamed: @"NSApplicationIcon"];
if (applicationIconImage) {
[NSApp setApplicationIconImage: applicationIconImage];
}
// Remember the ticket window state on quit.
[[KerberosPreferences sharedPreferences] setTicketWindowLastOpen: [self haveTicketListWindow]];
[minuteTimer invalidate];
}
// ---------------------------------------------------------------------------
- (void) preferencesDidChange: (NSNotification *) notification
{
dprintf ("Kerberos Controller got PreferencesDidChangeNotification");
[self synchronizeDockIcon];
[self menuNeedsUpdate: dockMenu];
//dprintf ("Kerberos Controller done with PreferencesDidChangeNotification");
}
// ---------------------------------------------------------------------------
- (void) cacheCollectionDidChange: (NSNotification *) notification
{
dprintf ("Kerberos Controller got CacheCollectionDidChangeNotification");
// Note: only put things here that do not depend on the ticket list.
// If it depends on the ticket list, use ticketListDidChange: instead.
[self synchronizeDockIcon];
[self menuNeedsUpdate: dockMenu];
[self menuNeedsUpdate: activeUserMenu];
//dprintf ("Kerberos Controller done with CacheCollectionDidChangeNotification");
}
// ---------------------------------------------------------------------------
- (void) ticketListDidChange: (NSNotification *) notification
{
dprintf ("Kerberos Controller got TicketListDidChangeNotification");
[self menuNeedsUpdate: ticketsMenu];
//dprintf ("Kerberos Controller done with TicketListDidChangeNotification");
}
// ---------------------------------------------------------------------------
- (void) cacheSelectionDidChange: (NSNotification *) notification
{
if (ticketListController == [notification object]) {
dprintf ("Kerberos Controller noticed KerberosCache List selection change...");
[self menuNeedsUpdate: ticketsMenu];
}
}
// ---------------------------------------------------------------------------
- (void) ticketSelectionDidChange: (NSNotification *) notification
{
if (ticketListController == [notification object]) {
dprintf ("Kerberos Controller noticed Ticket List selection change...");
[self menuNeedsUpdate: ticketsMenu];
}
}
// ---------------------------------------------------------------------------
- (void) windowWillClose: (NSNotification *) notification
{
if ((ticketListController != NULL) && ([ticketListController window] == [notification object])) {
// Okay, this is totally gross. Since there isn't an NSWindowDidCloseNotification,
// we have to do this by hand because the handy code in menuNeedsUpdate will call
// isVisible on the window and it's still visible when this gets called.
[renewTicketsMenuItem setEnabled: NO];
[validateTicketsMenuItem setEnabled: NO];
[destroyTicketsMenuItem setEnabled: NO];
[changePasswordMenuItem setEnabled: NO];
[showTicketInfoMenuItem setEnabled: NO];
dprintf ("Kerberos Controller noticed Ticket List Window closing...");
}
}
// ---------------------------------------------------------------------------
- (void) wakeFromSleep: (NSNotification *) notification
{
dprintf("KerberosController waking from sleep...");
// Since the computer was just asleep, credential renewal timers need
// to be adjusted to reflect the current time.
// Note that we don't want to post the notification directly from here
// because that would slow down the machine's wake from sleep process.
// Just queue it up.
NSNotification *credentialNotification = [NSNotification notificationWithName: KerberosCredentialTimersNeedResetNotification
object: self];
if (credentialNotification) {
[[NSNotificationQueue defaultQueue] enqueueNotification: credentialNotification
postingStyle: NSPostWhenIdle
coalesceMask: NSNotificationCoalescingOnName
forModes: NULL];
}
dprintf ("KerberosController done waking from sleep.");
}
#pragma mark -- Callbacks --
// ---------------------------------------------------------------------------
- (void) minuteTimer: (NSTimer *) timer
{
[self synchronizeDockIcon];
}
#pragma mark -- Ticket List State --
// ---------------------------------------------------------------------------
- (BOOL) haveTicketListWindow
{
return ((ticketListController != NULL) && ([[ticketListController window] isVisible]));
}
// ---------------------------------------------------------------------------
- (BOOL) ticketListWindowHasSelectedCache
{
return ([self haveTicketListWindow] && [ticketListController hasSelectedCache]);
}
// ---------------------------------------------------------------------------
- (BOOL) ticketListWindowSelectedCacheNeedsValidation
{
return ([self haveTicketListWindow] && [ticketListController selectedCacheNeedsValidation]);
}
// ---------------------------------------------------------------------------
- (BOOL) ticketListWindowHasSelectedCredential
{
return ([self haveTicketListWindow] && [ticketListController hasSelectedCredential]);
}
// ---------------------------------------------------------------------------
- (BOOL) haveDefaultCache
{
return ([cacheCollection defaultCache] != NULL);
}
// ---------------------------------------------------------------------------
- (BOOL) defaultCacheNeedsValidation
{
KerberosCache *defaultCache = [cacheCollection defaultCache];
return ((defaultCache != NULL) && [defaultCache needsValidation]);
}
#pragma mark -- Dock Icon --
// ---------------------------------------------------------------------------
- (void) synchronizeDockIcon
{
static BOOL sDockIconIsDynamic = NO; // Use so we don't refresh icon when not dynamic
KerberosCache *defaultCache = [cacheCollection defaultCache];
if ([[KerberosPreferences sharedPreferences] showTimeInDockIcon]) {
NSImage *iconImage = [self dockIconImageForCache: defaultCache];
if (iconImage) {
[NSApp setApplicationIconImage: iconImage];
}
sDockIconIsDynamic = YES;
} else {
if (sDockIconIsDynamic) {
NSImage *applicationIconImage = [NSImage imageNamed: @"NSApplicationIcon"];
if (applicationIconImage) {
[NSApp setApplicationIconImage: applicationIconImage];
sDockIconIsDynamic = NO; // remember so we don't keep doing this over and over
}
}
}
}
// ---------------------------------------------------------------------------
- (NSImage *) ticketsKerberosIconImage
{
if (!ticketsKerberosIconImage) {
NSString *iconPathString = [[NSBundle mainBundle] pathForResource: @"DockHasTickets"
ofType: @"tiff"];
if (iconPathString) {
ticketsKerberosIconImage = [[NSImage alloc] initWithContentsOfFile: iconPathString];
}
}
return ticketsKerberosIconImage;
}
// ---------------------------------------------------------------------------
- (NSImage *) warningKerberosIconImage
{
if (!warningKerberosIconImage) {
NSString *iconPathString = [[NSBundle mainBundle] pathForResource: @"DockTicketsWarning"
ofType: @"tiff"];
if (iconPathString) {
warningKerberosIconImage = [[NSImage alloc] initWithContentsOfFile: iconPathString];
}
}
return warningKerberosIconImage;
}
// ---------------------------------------------------------------------------
- (NSImage *) noTicketsKerberosIconImage
{
if (!noTicketsKerberosIconImage) {
NSString *iconPathString = [[NSBundle mainBundle] pathForResource: @"DockNoTickets"
ofType: @"tiff"];
if (iconPathString) {
noTicketsKerberosIconImage = [[NSImage alloc] initWithContentsOfFile: iconPathString];
}
}
return noTicketsKerberosIconImage;
}
// ---------------------------------------------------------------------------
- (NSImage *) kerberosAppIconImage
{
if (!kerberosAppIconImage) {
NSString *iconPathString = [[NSBundle mainBundle] pathForResource: @"KerberosApp"
ofType: @"icns"];
if (iconPathString) {
kerberosAppIconImage = [[NSImage alloc] initWithContentsOfFile: iconPathString];
}
}
return kerberosAppIconImage;
}
// ---------------------------------------------------------------------------
- (NSImage *) dockIconImageForCache: (KerberosCache *) cache
{
NSImage *newDockIconImage = NULL;
NSImage *baseImage = [self kerberosAppIconImage];
NSString *string = NULL;
if (cache) {
if ([cache state] == CredentialValid) {
string = [cache shortTimeRemainingString];
if ([cache timeRemaining] > kFiveMinutes) {
baseImage = [self ticketsKerberosIconImage];
} else {
baseImage = [self warningKerberosIconImage];
}
}
}
if (baseImage) {
NSSize iconSize = [[self kerberosAppIconImage] size];
newDockIconImage = [[[NSImage alloc] initWithSize: iconSize] autorelease];
if (newDockIconImage) {
[newDockIconImage lockFocus];
[baseImage setScalesWhenResized: YES];
[baseImage setSize: [newDockIconImage size]];
[baseImage compositeToPoint: NSMakePoint (0.0, 0.0) operation: NSCompositeSourceOver];
if (string) {
static NSDictionary *dockAttributes = NULL;
if (!dockAttributes) {
NSMutableParagraphStyle *style = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
[style setAlignment: NSLeftTextAlignment];
dockAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
[NSFont boldSystemFontOfSize: 22], NSFontAttributeName,
style, NSParagraphStyleAttributeName, NULL] retain];
}
NSSize textSize = [string sizeWithAttributes: dockAttributes];
// Add the time remaining string
[string drawAtPoint: NSMakePoint (((iconSize.width - textSize.width) / 2) + 19,
19.0 - (textSize.height / 2))
withAttributes: dockAttributes];
[newDockIconImage unlockFocus];
}
}
}
return newDockIconImage;
}
@end