Finish afscell implementation work
[openafs.git] / src / platform / DARWIN / afscell / afscellPane.m
index f8ac9b2..1b1ec29 100644 (file)
 //  afscell
 //
 //  Created by David Botsch on 10/23/07.
-//  Copyright (c) 2007 __MyCompanyName__. All rights reserved.
+//  Further developed by Toby Blake, April-May 2008
 //
 
 #import "afscellPane.h"
 
-
 @implementation afscellPane
 
+#pragma mark string constants
+NSString *fileReadThisCell=@"/private/var/db/openafs/etc/ThisCell";
+NSString *fileReadCellAlias=@"/private/var/db/openafs/etc/CellAlias";
+NSString *fileWriteThisCell=@"/private/tmp/org.OpenAFS.Install.ThisCell";
+NSString *fileWriteCellAlias=@"/private/tmp/org.OpenAFS.Install.CellAlias";
+
+#pragma mark private methods
+/* check line of CellAlias provided for Cell name, if matches, returns alias in third parameter */
+- (BOOL)_cellInCellAliasLine:(NSString *)line cellName:(NSString *)cell intoAlias:(NSString **)alias
+{
+       NSCharacterSet *alphaNum = [NSCharacterSet alphanumericCharacterSet];
+       NSScanner *cellScanner = [NSScanner scannerWithString:line];
+       if (![cellScanner scanUpToString:cell intoString:nil])
+       {
+               [cellScanner scanString:cell intoString:nil];
+               /* need to make sure cell name is followed by whitespace */
+               if (![cellScanner scanUpToCharactersFromSet:alphaNum intoString:nil])
+               {
+                       /* we have a match */
+                       /* get rest of string */
+                       [cellScanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:alias];
+                       return YES;
+               }       
+       }
+       return NO;
+}
+
+/* writes supplied line (plus newline) to string */
+- (void)_appendCellAliasLine:(NSString *)line toString:(NSMutableString *)outString
+{
+       [outString appendString:line];
+       [outString appendFormat:@"\n"];
+}
+
+/* writes supplied cell and alias, separated by space (plus newline at end) to string */
+- (void)_appendNewCellAliasLine:(NSString *)cell aliasName:(NSString *)alias toString:(NSMutableString *)outString
+{
+       [outString appendString:cell];
+       [outString appendFormat:@" "];
+       [outString appendString:alias];
+       [outString appendFormat:@"\n"];
+}
+
+/* displays (slightly) customisable alert */
+/* we don't use this method any more, but I'll leave it in just in case */
+- (BOOL) _continueAlert:(NSString *)message cancel:(BOOL)cancelButton
+{
+       BOOL returnValue = NO;
+       NSAlert *alert = [[NSAlert alloc] init];
+       [alert addButtonWithTitle:@"OK"];
+       (cancelButton) && [alert addButtonWithTitle:@"Cancel"];
+       [alert setMessageText:message];
+       [alert setAlertStyle:NSWarningAlertStyle];
+       
+       if ([alert runModal] == NSAlertFirstButtonReturn)
+       {
+               returnValue= YES;
+       }
+       [alert release];
+       return returnValue;
+}
+
+/* displays (slightly) customisable alert as a more attractive panel than _continueAlert above*/
+- (BOOL) _continueAlertPanel:(NSString *)message titleText:(NSString *)title firstButtonText:(NSString *)button1 secondButtonText:(NSString *)button2 
+{
+       if (NSRunInformationalAlertPanel(title, message, button1, button2, nil)
+               == NSAlertDefaultReturn)
+       {
+               return YES;
+       }
+       return NO;
+}
+
+- (BOOL) _validateStringWord:(NSString *)aString
+{
+       /* basic string validation to check it's got something in it, doesn't have whitespace or newline */
+       if (aString == nil || [aString length] == 0)
+       {
+               return NO;
+       }
+
+       if (([aString rangeOfString:@"\n"].location != NSNotFound) ||
+               ([aString rangeOfString:@"\t"].location != NSNotFound) ||
+               ([aString rangeOfString:@" "].location != NSNotFound))
+       {
+               return NO;
+       }
+       return YES;
+}
+
+- (BOOL) _validateCellString:(NSString *)cellString
+{
+       if (![self _validateStringWord:cellString])
+       {
+               return NO;
+       }
+
+       /* make sure it's in internet domain style, i.e. alphanum.alphanum */
+       NSCharacterSet *alphaNum = [NSCharacterSet alphanumericCharacterSet];
+       NSScanner *cellScanner = [NSScanner scannerWithString:cellString];
+       
+       /* first 'word' */
+       if (![cellScanner scanCharactersFromSet:alphaNum intoString:nil])
+       {
+               return NO;
+       }
+
+       /* anything that follows must be iteration of '.word' */
+       while (![cellScanner isAtEnd])
+       {
+               if (![cellScanner scanString:@"." intoString:nil])
+               {
+                       return NO;
+               }
+               if (![cellScanner scanCharactersFromSet:alphaNum intoString:nil])
+               {
+                       return NO;
+               }               
+       }
+       
+       return YES;
+}
+
+- (BOOL) _validateAliasString:(NSString *)aliasString
+{
+       if (![self _validateStringWord:aliasString])
+       {
+               return NO;
+       }
+
+       /* make sure it's alpha-numeric */
+       NSCharacterSet *alphaNum = [NSCharacterSet alphanumericCharacterSet];
+       NSScanner *aliasScanner = [NSScanner scannerWithString:aliasString];
+
+       if (![aliasScanner scanCharactersFromSet:alphaNum intoString:nil])
+       {
+               return NO;
+       }
+       /* if there's nothing but alpha-numeric, we should be at end */
+       if (![aliasScanner isAtEnd])
+       {
+               return NO;
+       }
+       
+       return YES;
+}
+
+#pragma mark InstallerPane overrides
 - (NSString *)title
 {
        return [[NSBundle bundleForClass:[self class]] localizedStringForKey:@"PaneTitle" value:nil table:nil];
 }
 
 
+/* called when user enters pane */
+- (BOOL)didEnterPane:(InstallerSectionDirection)dir
+{
+       /* get any existing values from ThisCell and CellAlias files */
+       NSString *readStrCellFile = [NSString stringWithContentsOfFile:fileReadThisCell encoding:NSASCIIStringEncoding error:nil];
+       NSString *readStrAliasFile = [NSString stringWithContentsOfFile:fileReadCellAlias encoding:NSASCIIStringEncoding error:nil];
+
+       /* drop out now if no ThisCell */
+       if (readStrCellFile == nil)
+       {
+               return YES;
+       }
+
+       NSString *cellString = @"";
+       NSString *aliasString = @"";
+
+       /* we only want the first line from ThisCell */
+       NSScanner *cellFileScanner = [NSScanner scannerWithString:readStrCellFile];
+
+       /* get value, removing any trailing whitespace */
+       [cellFileScanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:&cellString];
+
+       /* set Cell value in pane */
+       [ThisCell setStringValue:cellString];
+
+       /* drop out now if no CellAlias file */
+       if (readStrAliasFile == nil)
+       {
+               return YES;
+       }
+       
+       /* now find our local cell in the alias file */
+       NSScanner *lineScanner = [NSScanner scannerWithString:readStrAliasFile];
+       NSString *line;
+
+       /* get a line at a time, and check for Cell name */
+       while([lineScanner scanUpToString:@"\n" intoString:&line])
+       {
+               [self _cellInCellAliasLine:line cellName:cellString intoAlias:&aliasString];
+               [lineScanner scanString:@"\n" intoString:nil];
+       }
+
+       /* set Alias value in pane */
+       [CellAlias setStringValue:aliasString];
+
+       return YES;
+}
 
 /* called when user clicks "Continue" -- return value indicates if application should exit pane */
 - (BOOL)shouldExitPane:(InstallerSectionDirection)dir
 {
-       if(InstallerDirectionForward == dir) {
-               
+       if(InstallerDirectionForward == dir)
+       {
+               NSString * userName = NSUserName();
+
                /* Update ThisCell and CellAlias files with input */
-               NSString * WSCell = [ThisCell stringValue];
-               NSString * WSAlias = [CellAlias stringValue];
+               NSString * newStrCell = [ThisCell stringValue];
+               NSString * newStrAlias = [CellAlias stringValue];
+
+               if ([newStrCell length] == 0)
+               {
+                       return [self _continueAlertPanel:@"No local cell value specified.  Proceed with empty value?" titleText:@"No Local Cell" firstButtonText:@"Continue" secondButtonText:@"Cancel"];
+               }
                
+               /* do some validation on cell string */
+               if (![self _validateCellString:newStrCell])
+               {
+                       [self _continueAlertPanel:@"Invalid local cell name: value should be a complete Internet domain-style name (for example, \"abc.com\")" titleText:@"Invalid Cell Name" firstButtonText:@"OK" secondButtonText:nil];
+                       return NO;
+               }
                
-                                       
-                               BOOL wr1 = [WSCell writeToFile:@"/private/tmp/ThisCell" atomically:YES];
-                               BOOL wr2;
+               /* write value to ThisCell file */
                
-                               if ([WSAlias length] != 0) {
-                                       NSMutableString * aliasString = [[NSMutableString alloc] init];
-                                       [aliasString appendString:WSCell];
-                                       [aliasString appendFormat:@" "];
-                                       [aliasString appendString:WSAlias];
-                                       wr2 = [aliasString writeToFile:@"/private/tmp/CellAlias" atomically:YES];
-                               }
+               /* add username to file name */
+               NSMutableString * fileWriteThisCellUname = [NSMutableString stringWithCapacity:0];
+               [fileWriteThisCellUname appendString:fileWriteThisCell];
+               [fileWriteThisCellUname appendFormat:@"."];
+               [fileWriteThisCellUname appendString:userName];
+
+               NSMutableString * errorString = [NSMutableString stringWithCapacity:0];
+
+               NSMutableString * cellString = [NSMutableString stringWithCapacity:0];
+               [cellString appendString:newStrCell];
+               [cellString appendFormat:@"\n"];
+               if (![cellString writeToFile:fileWriteThisCellUname atomically:YES encoding:NSASCIIStringEncoding error:nil])
+               {
+                       [errorString setString:@"Could not write file: "];
+                       [errorString appendString:fileWriteThisCellUname];
+                       [errorString appendFormat:@"  Continue?"];
+                       if (![self _continueAlertPanel:errorString titleText:@"File Error" firstButtonText:@"Continue" secondButtonText:@"Cancel"])
+                       {
+                               return NO;
+                       }
+               }
+
+               if ([newStrAlias length] != 0)
+               {
+                       /* do some validation on alias string */
+                       if (![self _validateAliasString:newStrAlias])
+                       {
+                               [self _continueAlertPanel:@"Invalid alias: value should be alpha-numeric string" titleText:@"Invalid Alias" firstButtonText:@"OK" secondButtonText:nil];
+                               return NO;
+                       }
                
-                               NSMutableString * results = [[NSMutableString alloc] init];
-                               [results appendFormat:@"Write 1 is %d and write 2 is %d\n", wr1, wr2];
-                               [results writeToFile:@"/private/tmp/writefile" atomically:YES];
+                       BOOL processedAlias = NO;
+               
+                       /* read in alias file */
+                       NSString *readStrAliasFile = [NSString stringWithContentsOfFile:fileReadCellAlias encoding:NSASCIIStringEncoding error:nil];
+
+                       /* string to build up for writing out to file */
+                       NSMutableString * aliasString = [NSMutableString stringWithCapacity:0];
+
+                       if (readStrAliasFile != nil)
+                       {
+                               /* read in a line at a time */
+                               NSScanner *lineScanner = [NSScanner scannerWithString:readStrAliasFile];
+                               NSString *line;                                 
+                               NSString *alias = @"";
+
+                               while([lineScanner scanUpToString:@"\n" intoString:&line])
+                               {
+                                       /* check to see if we already have an alias for local cell */
+                                       if ([self _cellInCellAliasLine:line cellName:newStrCell intoAlias:&alias])
+                                       {
+                                               /* we have a match */
+                                               if ([alias isEqualToString:newStrAlias])
+                                               {
+                                                       /* write line as-is */
+                                                       [self _appendCellAliasLine:line toString:aliasString];
+                                               }
+                                               else
+                                               {
+                                                       /* write new cell-alias entry in place */
+                                                       [self _appendNewCellAliasLine:newStrCell aliasName:newStrAlias toString:aliasString];
+                                               }
+                                               processedAlias = YES;
+                                       }       
+                                       else
+                                       {
+                                               /* write line as-is */
+                                               [self _appendCellAliasLine:line toString:aliasString];
+                                       }
+                                       [lineScanner scanString:@"\n" intoString:nil];
+                               }
+                       }
+                       if (!processedAlias)
+                       {
+                               /* if we haven't written our line yet, append to end */
+                               [self _appendNewCellAliasLine:newStrCell aliasName:newStrAlias toString:aliasString];
+                       }
+
+                       /* add username to file name */
+                       NSMutableString * fileWriteCellAliasUname = [NSMutableString stringWithCapacity:0];
+                       [fileWriteCellAliasUname appendString:fileWriteCellAlias];
+                       [fileWriteCellAliasUname appendFormat:@"."];
+                       [fileWriteCellAliasUname appendString:userName];
                        
-               return YES;
+                       if (![aliasString writeToFile:fileWriteCellAliasUname atomically:YES encoding:NSASCIIStringEncoding error:nil])
+                       {
+                               [errorString setString:@"Could not write file: "];
+                               [errorString appendString:fileWriteCellAliasUname];
+                               [errorString appendFormat:@"  Continue?"];
+                               if (![self _continueAlertPanel:errorString titleText:@"File Error" firstButtonText:@"Continue" secondButtonText:@"Cancel"])
+
+                               {
+                                       return NO;
+                               }
+                       }
+               }
        }
        return YES;
 }