// SCSISelectorPanel.m
// SCSI Bus Inquirer  - NeXTStep 
//
// Copyright (C) 1990 by Jiro Nakamura and Canon Inc.
// Copyright (C) 1991, 1992 by Jiro Nakamura.
// All Rights Reserved.
//
// RCS Information
// Revision Number->	$Revision: 1.8 $
// Last Revised->	$Date: 92/11/13 23:32:23 $
//

static char rcsid[]="$Id: SCSISelectorPanel.m,v 1.8 92/11/13 23:32:23 jiro Exp Locker: jiro $";
static char copyrightid[]="$Copyright: Copyright (C) 1992 by Jiro Nakamura$";

#import "SCSISelectorPanel.h"
#import "SCSIControlWindow.h"
#import "SCSI.h"
#import <strings.h>
#import <appkit/Application.h>
#import <appkit/TextField.h>
#import <appkit/Matrix.h>
#import <appkit/Button.h>
#import <appkit/ButtonCell.h>
#import <appkit/Panel.h>		// For NX Run alert panel
#import <defaults/defaults.h>		// for NXWriteDefault() 
#import <appkit/publicWraps.h>		// for NXBeep()
#import <sys/types.h>
#import <libc.h>			// For geteuid()
#import <pwd.h>				// for password checking
#import <math.h>
#import <time.h>
#import <sys/vnode.h>		// for chmod code

#define SLEEPLOOP	50	/* Loop n times */
#define SLEEPTIME	10000	/* n microsecond sleep */

@implementation SCSISelectorPanel


void removeLastSpace( char *s, int length);



//
// Brings up the information panel.
//
- info:sender
{
    if (!infoPanel) 
	[NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
    [infoPanel orderFront:self];
    return self;
}

//
// Brings up the help panel.
//
- help:sender
{

	#ifdef DEBUG
		fprintf(stderr,"Help panel.\n");
	#endif
	
    if (!helpPanel) 
	[NXApp loadNibSection:"HelpPanel.nib" 
		owner:self withNames:NO];
    [helpPanel orderFront:self];
    return self;
}

//
// Brings up the legal panel.
//
- legal:sender
{
    if (!legalPanel) 
	[NXApp loadNibSection:"LegalPanel.nib" 
		owner:self withNames:NO];
    [legalPanel orderFront:self];
    return self;
}


- calculator:sender
{
        if( calculatorPanel == nil)
                {
                [NXApp loadNibSection: "Calculator.nib"
                        owner:self withNames: YES];
                }
    [calculatorPanel makeKeyAndOrderFront:self];
    return self;
}
	



- update
{
	struct stat buf;
    const char *sendTypes[3];
    const char *returnTypes[2];
	
	
	[super update];

    sendTypes[0] = NXAsciiPboardType;
    sendTypes[1] = NULL;
    sendTypes[2] = NULL;
    returnTypes[0] = NXAsciiPboardType;
    returnTypes[1] = NULL;
    [NXApp registerServicesMenuSendTypes:sendTypes andReturnTypes:returnTypes];


	if( geteuid() != 0)	// If we are not effectively root
		if( NXRunAlertPanel("Not running as Root", 
			"This program must be run as root "
			"in order to access all SCSI devices."
			"See manual for more information.", 
			"Continue", "Quit", NULL) == 0)
			exit(1);
	
	stat( NXArgv[0],  &buf);

	#ifdef DEBUG
		fprintf(stderr, "---- launching: euid = %d, uid= %d, "
			"Menu is nil = %d, NXArgv[0] = %s\n", 
			geteuid(), getuid(), installSelfMenuItem == nil, NXArgv[0]);
		fprintf(stderr,"Stat mode = %o, & 04000 = %o\n", 
			(unsigned int) buf.st_mode,	(unsigned int) buf.st_mode & 04000);
	#endif
	
	if( buf.st_mode & 04000 )
		{
		[installSelfMenuItem setTitle: "Already installed SET-UID"];
		}
	else if( installSelfMenuItem != nil &&
		( geteuid() == 0) && (getuid() == 0)
		&&  !(buf.st_mode & 04000 )) 	// really running as root
		{
			[installSelfMenuItem setEnabled: YES];
		}
	
			
	return self;
}

- installSelf: sender
{
	struct stat buf;

	if(getuid() != 0)
		{
		NXRunAlertPanel("Not running as root",
			"Installation can only be done by root. Please "
			"login as root and reinstall.", "OK", NULL, NULL);
		[installSelfMenuItem setEnabled: NO];
		return self;
		}
		
	if( NXRunAlertPanel("Install self as SET-UID root", 
		"Installing this program as SET-UID root allows any user "
		"to run this program as root and represents a security "
		"hazard for some sites. Please read the documentation for more "
		"details.", "Install", "Cancel", NULL) == 0)
		return nil;
	
	stat( NXArgv[0],  &buf);
	chown( NXArgv[0], 0, 3);
	chmod( NXArgv[0], VSUID | buf.st_mode);
	fprintf( stderr, "%s: Set owner to root.bin (0.3), SET-UID root.\n",
		[NXApp appName]);
	
	[installSelfMenuItem setTitle: "Already installed SET-UID"];
	[installSelfMenuItem setEnabled: NO];
	
	NXRunAlertPanel("Installation Successful", "Program installed "
		"successfully.", "OK", NULL, NULL);
		
	return self;
}
	
	
- appDidInit: sender
{
	static NXDefaultsVector myDefaults =
		{
			{ "License",		"0000000000"},
			{ "Key",		"-"},
			{ NULL}
		};
	NXRegisterDefaults( [NXApp appName], myDefaults);
	return [self inquireAll: self];
}


- inquireAll:sender
{
	int target, tmp, tmp2, num_mp;
	FILE *mp;
	struct mntent  *mepp; 		// mount entry pointer
	struct mountentry mep[7];
	id 	titles[8] = {Title0, Title1, Title2, Title3, Title4,  
			Title5, Title6, Title7};

	struct passwd entry;
	const char *guess;
	char *cp;
	static BOOL check = NO, modal = NO;


	// if we are sticky root then we should verify
	if( getuid() != 0 && geteuid() == 0 && check == NO) 
		{
		entry = * getpwuid(0);
		guess = [passwordText stringValue];
		cp = crypt ((char *) guess, entry.pw_passwd);
  		if (strcmp (cp, entry.pw_passwd))
			{
			NXBeep();
			check = NO;
			[passwordPanel makeKeyAndOrderFront: self];
			[passwordText setStringValue: ""];
			[passwordText setTextGray: NX_WHITE];
			[passwordText selectText: self];
			if( !modal )
				{
				modal  = YES;
				[NXApp runModalFor: passwordPanel];
				}
			return nil;
			}
		else
			{
			if( modal )
				[NXApp stopModal];
			modal = NO;
			[passwordPanel close];
			check = YES;	
			
			}
		} 
	

	for( target = 0; target < 7; target ++)
		[self inquireTarget: target];
	

	#ifdef DEBUG 
		fprintf(stderr,"Figuring out mount entries.\n" );
	#endif DEBUG
	
	if(	(mp = setmntent( "/etc/mtab", "r")) == NULL )
		{
		NXRunAlertPanel("File system error", "Could not open "
			"mount table.", "Quit", NULL, NULL );
		exit(1);
		}
	
	
	num_mp = 0;	
	for(; (mepp = getmntent( mp )) != NULL; )
		{
		strcpy( mep[num_mp].mnt_fsname, mepp->mnt_fsname );
		strcpy( mep[num_mp].mnt_dir, mepp->mnt_dir );
		strcpy( mep[num_mp].mnt_type, mepp->mnt_type );
		strcpy( mep[num_mp].mnt_opts, mepp->mnt_opts );
		mep[num_mp].mnt_freq = mepp->mnt_freq;
		mep[num_mp].mnt_passno = mepp->mnt_passno;
		
		#ifdef DEBUG
			fprintf(stderr, "%d: Fsname = <%s>, dir = <%s>\n",
			num_mp, mep[num_mp].mnt_fsname, mep[num_mp].mnt_dir );
		#endif
		
		if( strncmp( mep[num_mp].mnt_fsname, "/dev/sd", 7 ) == 0 ||
			strncmp( mep[num_mp].mnt_fsname, "/dev/rsd", 8 ) == 0)
			{
			#ifdef DEBUG 
				fprintf(stderr, "Mount point %d:%s is SCSI\n",
					num_mp, mep[num_mp].mnt_fsname);
				fprintf(stderr, "Type = <%s>, opts = <%s>\n",
					mep[num_mp].mnt_type, 
					mep[num_mp].mnt_opts);
			#endif
			num_mp ++;
			}
		}
	endmntent ( mp );

	tmp = 0;

	for( target = 0; target < 7; target ++)
		{
		devInfo[target].mounted = NO;
		
		if( devInfo[target].deviceValid && (
			devInfo[target].ir.ir_devicetype == DEVTYPE_DISK ||
			devInfo[target].ir.ir_devicetype == DEVTYPE_OPTICAL ||
			devInfo[target].ir.ir_devicetype ==  DEVTYPE_CDROM
			))
			{
			sprintf(devInfo[target].rawDevice, 
				"/dev/rsd%dh", tmp);
			sprintf(devInfo[target].blockDevice, 
				"/dev/sd%da", tmp);
			tmp ++;

			#ifdef DEBUG
				fprintf(stderr, "Target %d is raw disk <%s>\n",
				target, devInfo[target].rawDevice );
			#endif
		
			devInfo[target].mounted = NO;
			
			for( tmp2 = 0; tmp2 < num_mp; tmp2 ++ )
				{
				if( strcmp( devInfo[target].blockDevice, 
					mep[tmp2].mnt_fsname) == 0 || 
					strcmp( devInfo[target].rawDevice, 
					mep[tmp2].mnt_fsname) == 0)
					{
					devInfo[target].mounted = YES;
					#ifdef DEBUG
						fprintf(stderr,
						    "Drive <%s> mounted!  "
					   	     "Mount type = <%s>, "
						    "Opts = <%s>\n"
						     ,
						  mep[tmp2].mnt_fsname,
						     mep[tmp2].mnt_type,
							mep[tmp2].mnt_opts);
					#endif
					break;
					}
				}
			}
				
		if( 	devInfo[target].deviceValid  && 
			[controlWindow acceptsDeviceType: 
			(int) devInfo[target].ir.ir_devicetype
		 	andMounted: devInfo[target].mounted ] )
			{
			if( devInfo[target].mounted )
				[titles[target] setTextGray: NX_DKGRAY];
			else
				[titles[target] setTextGray: NX_BLACK];
			}
		else
			{
			[titles[target] setTextGray: NX_WHITE];
			}
			
		}	
		
	return self;
}

- inquireTarget: (int) target
{
	id 	titles[8] = {Title0, Title1, Title2, Title3, Title4,  
			Title5, Title6, Title7};
	
	SCSI	*scsi;
	struct inquiry_reply ibuffer;
	static char vendorBuffer[50], typeBuffer[20];
	static char tiffBuffer[25];
	
	scsi  = [SCSI new];
	[scsi openSCSI];

	if( [scsi setTarget: target lun: 0] == 0 )
		{
		int sleeper, error;
		
		for( sleeper = 0; sleeper < SLEEPLOOP; sleeper ++ )
			{
			error = [scsi inquirySCSI: &ibuffer];
			#ifdef DEBUG
				fprintf(stderr,"%d: Targ %d, error = %d\n", 
					sleeper,  target,error
					);
			#endif
					
			if( error == SR_IOST_GOOD )	// no error
				break;
			
			if(  error == SR_IOST_SELTO )	// no device
				{
				sleeper = -1;    // fake no device
				break;
				}
					
			usleep( SLEEPTIME );
			}
			
		if(  sleeper < SLEEPLOOP && sleeper != -1)
			{
			#ifdef DEBUG
				fprintf(stderr, 
					"Target = %d   Lun = %d\n"
					"Device type  = 0x%x\n"
					"ANSI Version Number     = %d\n"
					"Additional List Length  = %d\n"
					"Vendor ID    = <%.8s>\n" 
					"Product ID   = <%.16s>\n"
					"Revision     = <%.4s>\n"
					"-----------------------\n", 
					target, 0, 
					(unsigned int) ibuffer.ir_devicetype, 
					ibuffer.ir_ansiversion,
					 ibuffer.ir_addlistlen,
					ibuffer.ir_vendorid, 	
					ibuffer.ir_productid, 
					ibuffer.ir_revision);
			#endif
			
			sprintf(vendorBuffer,"%.8s\n%.16s", 
				ibuffer.ir_vendorid, 
				ibuffer.ir_productid);
							
			sprintf(devInfo[target].vendorID, "%.8s",
				ibuffer.ir_vendorid);

			sprintf(devInfo[target].productID,"%.16s",
				ibuffer.ir_productid);
			removeLastSpace( devInfo[target].vendorID, 8);
			removeLastSpace( devInfo[target].productID, 16);
				

			strcpy( typeBuffer, 
				[scsi identifyDeviceType: &ibuffer]);
			
			devInfo[target].removable = ibuffer.ir_removable;
			devInfo[target].deviceValid = YES;
			devInfo[target].ir = ibuffer;
			}
		else
			{
			sprintf(vendorBuffer, "No device");
			sprintf(typeBuffer, "No-Device");
			devInfo[target].deviceValid = NO;
			}
		}
	else
		{
		sprintf(vendorBuffer, "Cannot access");
		sprintf(typeBuffer, "Cannot-Access");
		devInfo[target].deviceValid = NO;
		}
			
	[titles[target] setStringValue: vendorBuffer];
	
	sprintf(tiffBuffer,"%s.tiff", typeBuffer);
	
	[[deviceButtonMatrix findCellWithTag: target] setIcon: typeBuffer];
		
	NXPing();
	[scsi closeSCSI];
	[scsi free];
	
	return self;
}

- inquireSelected: sender
{
	int t;

	if( [self	inquireTarget: [deviceButtonMatrix selectedTag]] 
		== nil) 	// no device found
		{
		NXRunAlertPanel ("No device","There is no SCSI device "
			"at this target number", "OK", NULL, NULL);
		[controlWindow close];
		return nil;
		}
		
	t = [deviceButtonMatrix selectedTag];	// target
	
	if( [controlWindow  acceptsDeviceType: [self targetDeviceType: t]
		andMounted: [self targetIsMounted: t]] &&
		[controlWindow setTarget:  t lun: 0])
		{
		[controlWindow makeKeyAndOrderFront: self];
		}
	else	
		{
		[controlWindow close];
		NXBeep();
		return nil;
		}
	
	if( controlWindow == nil) 
		{
		NXRunAlertPanel("License Expiration", "Your evaluation "
			"copy has expired. Please contact your distributor "
			"for a full license.", "Quit", NULL, NULL );
		exit(1);
		}
	 return self;
}

- (BOOL) targetIsMounted: (int) targ	{ return devInfo[targ].mounted; }
- (BOOL) targetRemovable: (int) targ	{ return devInfo[targ].removable; }

- (int) targetDeviceType: (int) targ	
	{ return devInfo[targ].ir.ir_devicetype; }
- (char *) targetRawDevice: (int) targ
	{ return  devInfo[targ].rawDevice; }
- (char *) targetVendorID: (int) targ
	{ return  devInfo[targ].vendorID; }
- (char *) targetProductID: (int) targ
	{ return  devInfo[targ].productID; }
@end
