windows-nt/Source/XPSP1/NT/net/sfm/uam/uam5src/uamsrc/uammain.c
2020-09-26 16:20:57 +08:00

1 line
32 KiB
C

// ===========================================================================
// UAMMain.cp © 1997-2000 Microsoft Corp. All rights reserved.
// ===========================================================================
// Main unit for Microsoft User Authentication Method.
//
// Notes:
// --------------------------------------------------------------------------
//
//
// Version History:
// ===========================================================================
// 04.22.97 MJC - Begin coding version 5.0.
// 05.22.97 MJC - Completed version 5.0d5, getting close.
// 02.21.98 MJC - Begin updating for AppleShare 3.8 and AFP/TCP.
// 03.02.98 MJC - First working version (5.0d12) with AS Client v3.8a1lawJ
// 03.26.98 MJC - Implemented change password. It works, but not when the
// password has expired on the server. I suspect a bug in
// in AppleShare Client 3.8a1LawL which doesn't open a session
// when the error code returned is not noErr.
// 03.31.98 MJC - First checkin into VSS database.
//
// Version 5.0d15:
// 04.13.98 MJC - Changed the way supported UAM's are recorded (bitmap vice
// a struct of booleans).
// - Change some error code determination code in UAM_OpenSession()
// and UAM_MSUAMContLogin().
// - Added version string at bottom of dialog window.
//
// Version 5.0d16:
// 04.30.98 MJC - Fixed bug in UAMDSNetwork.c where the AFP login command block
// would not always end on an even boundary.
// - Added some additional asserts to UAM_DSLoginMSUAM().
// - Changed instances of astring[0] to PSTR_LENGTH(astring).
//
// Version 5.0d17:
// 05.19.98 MJC - Updated for new ClientUAM.h from Apple. Now the main
// entry returns OSStatus vice OSErr.
//
// Version 5.0b2:
// 06.08.98 MJC - Added new event callback routine for AS and the Chooser
// in the login dialog filter.
// Version 5.0b3:
// 09.01.98 MJC - Fixed bug where null passwords weren't allowed.
// 10.23.98 MJC - Fixed bug where you could use cmd-g to select Guest
// radio even though it was disabled.
// - Can now use cut, copy and paste in User Name field.
// - Changed 'OK' button to 'Connect' to match Apple's UAM
// - Clicking on 'Registered User' when it is already
// doesn't cause a flash anymore or select the user name.
// 11.13.98 MJC - Added support for passing the actual encrypted password
// over the wire for cleartxt storage updating when
// changing password.
// MJC - Added support for notifying the user that their password
// is about to expire.
// 12.01.98 MJC - Fixed bug were I wasn't reversing the byte order of the
// returned password expiration time.
// 01.22.99 MJC - CheckGatedControls() would step 1 too far in the array.
// - Could not use escape key if username len maxed out.
// Version 5.0.1:
// 07.12.99 MJC - More problems with UAM_CheckGatedControls(), hopefully all
// fixed this time.
// Made small change in MS_VersionUserItem() so we compile
// under CW Pro 5.
// Version 5.0.2:
// 10.21.99 MJC - Fixed bug on double byte character OS's (CHX, JPN, etc)
// where first char in password was getting dropped.
// - Now select all the password text after a login failure.
// Version 5.0.3:
// 10.29.99 MJC - Fixed bug on international systems where hitting
// backspace would yield incorrect results (got rid of one
// char instead of the double byte char).
// - Related to fix above, change password field entry diaplay
// character to '*' instead of '¥'.
// Version 5.0.4:
// 11.17.99 MJC - Fixed bug in encrypt.c, wasn't locking resource handle,
// so password OWF was incorrectly generated.
// - SetupUAMEncrypt() was not returning a fail code
// if loading the data table failed.
// Version 5.0.5:
// 11.22.99 MJC - Put 2 0x00 bytes at the end of the initial login call for
// NT4 SP6.
// 12.01.99 MJC - Finished keychain support.
// - NOTE: You must now compile the MS UAM with Universal
// headers v3.3 or later.
// - Can finally build PPC! The MS UAM is now a safe FAT
// binary. So, it'll run natively on 68K and PPC.
// - Complete rewrite of password edit field handling. Now kicks
// butt! You can type just like any other text and should work
// better with foreign languages.
// - Made some changes to the dialog code in preparation
// for Carbon.
// 01.10.00 MJC - Now check for cmd key down when opening UAM so user can
// bypass keychain.
// 03.13.00 MJC - Removed about dialog.
// 03.15.00 MJC - Now check for MacOS 9 or > to see if keychain is available.
// - Now week load the Keychain.lib for compatibility with
// older systems.
// 03.20.00 MJC - Fixed bug: When changing password, wasn't checking for existance
// of keychain manager (caused -2802 error).
// Version 5.0.6:
// 06.11.00 MJC - Now give the option to replace keychains items that
// already exist. This caused problems when the user changed
// their password on another machine, there was no way to
// update the keychain item without doing it manually from the
// KeychainAccess control panel.
// Version 5.0.7:
// 09.06.00 MJC - Bug fix: keychain item shouldn't appear when guest selected
// - Bug fix: Don't allow white space as first char in user name, this
// involved redoing the gating logic in UAMDlogUtils.c.
// 09.28.00 - Bug fix: Allow null user name and password entries when
// guest login is enabled on server.
// ===========================================================================
#include <A4Stuff.h>
#include <SetupA4.h>
#include "UAMMain.h"
#include "UAMDebug.h"
#include "UAMUtils.h"
#include "UAMDialogs.h"
#include "UAMNetwork.h"
#include "UAMDSNetwork.h"
#include "UAMDLOGUtils.h"
#include "UAMKeychain.h"
//
//Global variables are declared here
//
Str32 gServerName;
Str32 gUserName;
Boolean gContextInited;
Boolean gGuestLogon;
Boolean gSupportsChngPwd;
Boolean gDoingIPConnection;
DialogPtr gDialog;
Str32 gAFPVersion;
long gSupportedUAMs;
ModalFilterUPP gDialogFilter;
ModalFilterUPP gPwdDialogFilter;
UserItemUPP gLineItem;
UserItemUPP gVersionItem;
Str32 gUAMVersionString;
Str32 gZoneName;
UInt32 gExpirationTime = 0;
OTAddress* gServerAddress = NULL;
EventCallbackPtr gEventCallbackUPP = NULL;
Boolean gTriedKeychain = false;
#if GENERATINGCFM
//We need to define __procinfo for Metrowerks' linker. This basically
//defines main. Without it, we'll get a link error.
ProcInfoType __procinfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UAMArgs*)));
#endif
// ---------------------------------------------------------------------------
// ¥ main()
// ---------------------------------------------------------------------------
// This is the main entry point for our UAM. This function is passed a
// pointer to a UAMArgs struct. This struct contains the function selector,
// call-backs, and many other things we need to do our stuff.
pascal OSStatus main(UAMArgs *inUAMArgs)
{
OSStatus theResult = noErr;
EnterCodeResource();
PrepareCallback();
switch(inUAMArgs->command)
{
case kUAMOpen:
theResult = MS_UAMOpen(inUAMArgs);
break;
case kUAMClose:
MS_UAMClose();
break;
case kUAMPWDlog:
theResult = MS_UAMPwdDialog(inUAMArgs);
break;
case kUAMLogin:
theResult = MS_UAMLogin(inUAMArgs);
break;
case kUAMVSDlog:
break;
case kUAMChgPass:
case kUAMChgPassDlg:
DbgPrint_((DBGBUFF, "Change password dialog must be implemented"));
theResult = kNotForUs;
break;
default:
//
//If we get here then we were asked to handle a routine that
//we don't support. Return the appropriate error code.
//
DbgPrint_((DBGBUFF, "Unsupported function selector in MSUAM main() (%d)", inUAMArgs->command));
theResult = kNotForUs;
break;
}
ExitCodeResource();
return(theResult);
}
// ---------------------------------------------------------------------------
// ¥ MS_UAMOpen()
// ---------------------------------------------------------------------------
// This is called by the device package. It is not a required function but
// we use it to initialize our UAM code. Note that when we encounter an
// error we don't make an effort to clean up. Instead we return userCanceledErr
// in which case our UAMClose function will be called by AppleShare Client.
OSStatus MS_UAMOpen(UAMArgs *inUAMArgs)
{
short theUAMConfig = 0;
//
//Get the name of the server we want to log into.
//
UAM_PStrCopy(inUAMArgs->Opt.open.objectName, gServerName);
//
//Copy the zone name for. If it's NULL, then we
//don't have a zone name.
//
if (inUAMArgs->Opt.open.zoneName != NULL)
UAM_PStrCopy(inUAMArgs->Opt.open.zoneName, gZoneName);
else
gZoneName[0] = 0;
gContextInited = false; //Been through PwdDialog before?
gGuestLogon = false; //Is guest our logon choice?
gDoingIPConnection = false; //Default to AppleTalk support.
gDialog = NULL; //So we can see if we really got it.
gDialogFilter = NULL;
gPwdDialogFilter = NULL;
gLineItem = NULL;
gVersionItem = NULL;
gAFPVersion[0] = 0;
gUserName[0] = 0;
gServerAddress = inUAMArgs->Opt.open.srvrAddress;
gEventCallbackUPP = inUAMArgs->callbacks->EventCallbackUPP;
gTriedKeychain = false;
UAM_KCInitialize(inUAMArgs);
//
//Under PowerPC this is a pointer allocated. Under 68K, it just
//points to the function.
//
gDialogFilter = NewModalFilterProc(&UAM_DialogFilter);
if (gDialogFilter == NULL)
{
//
//We check for ptr validity. Note that we don't bother to
//clean up since we'll get a kUAMClose message next.
//
DbgPrint_((DBGBUFF, "Failed to allocate gDialogFilter"));
return(userCanceledErr);
}
gPwdDialogFilter = NewModalFilterProc(&MS_PwdDialogFilter);
if (gPwdDialogFilter == NULL)
{
DbgPrint_((DBGBUFF, "Failed to allocate gPwdDialogFilter"));
return(userCanceledErr);
}
gLineItem = NewUserItemProc(&UAM_FrameItem);
if (gLineItem == NULL)
{
DbgPrint_((DBGBUFF, "Failed to allocate gLineItem"));
return(userCanceledErr);
}
gVersionItem = NewUserItemProc(&MS_VersionUserItem);
if (gVersionItem == NULL)
{
DbgPrint_((DBGBUFF, "Failed to allocate gVersionItem"));
return(userCanceledErr);
}
//
//Get the AFP version and the default user name. This function finds
//a match which is the highest AFP version supported by both the client
//and server.
//
UAM_GetAFPVersionString(
inUAMArgs->Opt.open.srvrInfo,
inUAMArgs->callbacks,
gAFPVersion,
gUserName );
//
//gUserName can be null, we just capture here during debugging to
//ensure we're getting the name properly.
//
Assert_(PSTR_LENGTH(gUserName) != 0);
Assert_(PSTR_LENGTH(gAFPVersion) != 0);
if (PSTR_LENGTH(gAFPVersion) == 0)
{
//
//No AFPVersion, no logon...
//
UAM_ReportError(uamErr_NoAFPVersion);
return(userCanceledErr);
}
gSupportsChngPwd = ((inUAMArgs->Opt.open.srvrInfo->fFlags & kSupportsChngPswd) != 0);
//
//Determine what connection method we are using, IP or AppleTalk. Basically,
//if the client supports IP and the address type is IP, then we have
//a TCP connection.
//
if (inUAMArgs->Opt.open.srvrInfo->fFlags & kSupportsTCPIP)
{
if (inUAMArgs->Opt.open.srvrAddress->fAddressType == AF_INET)
{
gDoingIPConnection = TRUE;
}
}
//
//Get the list of supported UAMs from a utility routine. This data
//is necessary in the password dialog code.
//
UAM_GetSupportedUAMS(
(ServerInfoReplyBlock *)inUAMArgs->Opt.open.srvrInfo,
&gSupportedUAMs );
//
//We should never get here if the following is false, but we
//check just to be on the safe side.
//
if ( ((gSupportedUAMs & kMSUAMSupported) == 0) &&
((gSupportedUAMs & kMSUAM_V2_Supported) == 0) )
{
Assert_((gSupportedUAMs & kMSUAMSupported) != 0);
UAM_ReportError(afpBadUAM);
return(userCanceledErr);
}
UAM_VersionString(gUAMVersionString);
//
//This is how we tell AppleShare what our UAM supports. We have
//our own password dialog, we support change password, and we
//use our own change password dialog.
//
theUAMConfig |= BIT_0; //Custom login dialog
theUAMConfig |= BIT_2; //We support change password
theUAMConfig |= BIT_3; //Custom change password dialog
inUAMArgs->result = theUAMConfig;
return(noErr);
}
// ---------------------------------------------------------------------------
// ¥ MS_UAMClose()
// ---------------------------------------------------------------------------
// Like UAMOpen, UAMClose has no specific purpose as defined by the device
// manager. We use it to clean up our allocated storage and globals.
void MS_UAMClose(void)
{
if (gDialog != NULL)
{
//
//If we put up our login dialog, get rid of it.
//
UAM_DisposeDialog(gDialog);
}
if (gDialogFilter != NULL) DisposeRoutineDescriptor(gDialogFilter);
if (gLineItem != NULL) DisposeRoutineDescriptor(gLineItem);
if (gPwdDialogFilter != NULL) DisposeRoutineDescriptor(gPwdDialogFilter);
if (gVersionItem != NULL) DisposeRoutineDescriptor(gVersionItem);
}
// ---------------------------------------------------------------------------
// ¥ MS_VersionUserItem()
// ---------------------------------------------------------------------------
// Custom user item routine to display UAM version number.
pascal void MS_VersionUserItem(DialogPtr inDialog, DialogItemIndex inItem)
{
short theFont, theSize;
Rect theItemRect;
EnterCallback();
theFont = inDialog->txFont;
theSize = inDialog->txSize;
TextFont(kFontIDGeneva);
TextSize(9);
theItemRect = UAM_GetItemRect(inDialog, inItem);
TETextBox(
&gUAMVersionString[1],
PSTR_LENGTH(gUAMVersionString),
&theItemRect,
teJustRight );
TextFont(theFont);
TextSize(theSize);
ExitCallback();
}
// ---------------------------------------------------------------------------
// ¥ MS_PwdDialogFilter()
// ---------------------------------------------------------------------------
// Filter function for the password dialog. We have this so we can capture
// command keys and keep length requirements for the user name in the login
// dialog.
pascal Boolean MS_PwdDialogFilter(DialogPtr inDialog, EventRecord *inEvent, short *inItem)
{
short theCode;
Str255 theString;
Boolean theResult = false;
EnterCallback();
if (inEvent->what == keyDown)
{
theCode = (inEvent->message & charCodeMask);
if (inEvent->modifiers & cmdKey)
{
switch(theCode)
{
case 'g':
case 'G':
*inItem = DITEM_GuestRadio;
theResult = true;
break;
case 'r':
case 'R':
*inItem = DITEM_RegRadio;
theResult = true;
break;
case 's':
case 'S':
*inItem = DITEM_ChangePwd;
theResult = true;
break;
case 'a':
case 'A':
*inItem = DITEM_Keychain;
theResult = true;
break;
//
//Handle edit commands from the user. We don't allow any
//editing commands in the password field. This mimicks
//Apple's own UAM's.
//
case 'c':
case 'C':
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
DialogCopy(inDialog);
}
break;
case 'v':
case 'V':
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
DialogPaste(inDialog);
}
break;
case 'x':
case 'X':
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
DialogCut(inDialog);
}
break;
default:
break;
}
}
else
{
//
//Don't allow more than UAM_USERNAMELEN maximum characters in edit field.
//
if ((((DialogPeek)inDialog)->editField + 1) == DITEM_UserName)
{
UAM_GetText(inDialog, DITEM_UserName, (Str255 *)&theString);
switch(theCode)
{
case UAMKey_BackDel:
case UAMKey_Left:
case UAMKey_Right:
case UAMKey_Return:
case UAMKey_Enter:
case UAMKey_Escape:
break;
default:
if (PSTR_LENGTH(theString) >= UAM_USERNAMELEN)
{
SysBeep(1);
inEvent->what = nullEvent;
theResult = true;
}
break;
}
}
}
}
else
{
if (gEventCallbackUPP)
{
//
//If we're not handling the event ourselves, then call the
//event callback which gives AS and the Chooser a chance
//to update it's windows, etc.
//
#if GENERATING68K
gEventCallbackUPP(inEvent);
#else
CallUniversalProc(gEventCallbackUPP, kEventCallbackProcInfo, inEvent);
#endif
}
}
ExitCallback();
return(theResult);
}
// ---------------------------------------------------------------------------
// ¥ MS_UAMPwdDialog()
// ---------------------------------------------------------------------------
// This is where we put up our password dialog. The buffers pointed to by
// 'inUserName' and 'inPassword' end up getting passed directly to the
// UAMLogin function.
//
// The buffer passed for the user name and password is 64 bytes long. Don't
// use more than that!
OSStatus MS_UAMPwdDialog(UAMArgs *inUAMArgs)
{
short theItem, x;
Str255 theStr;
OSStatus theError = noErr;
Boolean theLoop = true;
Assert_(gDialogFilter != NULL);
Assert_(gPwdDialogFilter != NULL);
Assert_(gLineItem != NULL);
//
//Determine which user name to use, the default or the
//one supplied by the client (if any). gUserName is filled
//in originally during the UAMOpen call.
//
if (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName) != 0)
{
UAM_PStrCopy(inUAMArgs->Opt.pwDlg.userName, gUserName);
}
//
//If we already tried the keychain and failed, we don't want
//to try again or we'll loop forever. Give the user a chance
//to enter the correct name and password.
//
//NOTE: We check to see if the cmd key is down, if it is, then
//we bypass the keychain stuff alltogether. Maybe the user wants
//to change his password!?!?!
//
if ((gTriedKeychain == false) && (UAM_KCAvailable()) && (!UAM_KeyDown(KEY_Command)))
{
gTriedKeychain = true;
if ( (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName)) &&
(PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password)) )
{
//
//We were supplied a username and password by the AFP
//client. This means the user clicked a keychain entry.
//
goto exit;
}
else
{
theError = UAM_KCFindAppleSharePassword(
gUserName,
inUAMArgs->Opt.pwDlg.password,
gServerName,
NULL
);
if (theError == noErr)
{
DbgPrint_((DBGBUFF, "Pswd found via MSUAM keychain calls;g"));
//
//Fill in the user name for the UAMArgs.
//
if (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName) == 0)
{
UAM_PStrCopy(gUserName, inUAMArgs->Opt.pwDlg.userName);
}
//
//A password was found so try to logon.
//
goto exit;
}
else if ( (theError != errKCItemNotFound) &&
(theError != userCanceledErr) )
{
//
//Only report "real" errors.
//
UAM_ReportError(theError);
}
}
}
else if ((UAM_KCAvailable()) && (UAM_KeyDown(KEY_Command)))
{
//
//If the user is holding the cmd key down, then we don't want to
//try the keychain the next time through either.
//
gTriedKeychain = true;
}
//
//Display the server name in the dialog title text
//which is located at the top of the dialog. This must be
//done even if we've been here before.
//
ParamText(gServerName, NULL, NULL, NULL);
//
//If we haven't been through here before, then we need to do
//all the prep work.
//
if (!gContextInited)
{
gDialog = UAM_NewDialog(DLOG_Login, true);
if (gDialog == NULL)
{
//
//If we couldn't get the dialog, then we're either out
//of memory or the resource couldn't be found.
//
theError = MemError();
if (theError == noErr)
theError = ResError();
if (theError == noErr)
theError = resNotFound;
UAM_ReportError(theError);
return(userCanceledErr);
}
//
//Set up the default user name and password (if any). If a user name
//exists, then make the password field the active field ready for input.
//
UAM_SetUpUserItem(gDialog, DITEM_Line, gLineItem, userItem);
UAM_SetUpUserItem(gDialog, DITEM_Version, gVersionItem, userItem);
//
//Put in some extra info in the dialog for debugging.
//
#ifdef UAMDebug
Str255 theConnString;
if (gDoingIPConnection)
UAM_PStrCopy(PSTR_TCPConnection, theConnString);
else
UAM_PStrCopy(PSTR_AppleTalkConnection, theConnString);
if (gSupportedUAMs & kMSUAM_V2_Supported)
UAM_AppendPStr(theConnString, "\p (MS2.0)", sizeof(Str255));
else
UAM_AppendPStr(theConnString, "\p (MS1.0)", sizeof(Str255));
#if GENERATINGCFM
UAM_AppendPStr(theConnString, "\p[PPC]", sizeof(Str255));
#else
UAM_AppendPStr(theConnString, "\p[68K]", sizeof(Str255));
#endif
#endif
//
//Let the client know what connection method is being used to
//connect to the server.
//
if (gDoingIPConnection)
{
#ifdef UAMDebug
UAM_SetText(gDialog, DITEM_Method, theConnString);
#else
UAM_SetText(gDialog, DITEM_Method, PSTR_TCPConnection);
#endif
}
else
{
#ifdef UAMDebug
UAM_SetText(gDialog, DITEM_Method, theConnString);
#else
UAM_SetText(gDialog, DITEM_Method, PSTR_AppleTalkConnection);
#endif
}
//
//If we've not been here before, then we want to use the user name
//entered in the Sharing Setup Control Panel (or Chooser).
//
if (PSTR_LENGTH(gUserName) != 0)
{
UAM_SetText(gDialog, DITEM_UserName, gUserName);
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
}
else
{
UAM_HiliteItem(gDialog, 1, 255);
}
//
//Now we set up the guest and registered user radio buttons and the
//change password button as determined by UAM_GetServerInfo().
//
if (!gSupportsChngPwd) {
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
}
else {
UAM_GateControl(gDialog, DITEM_ChangePwd, DITEM_UserName);
}
if (!(gSupportedUAMs & kGuestSupported))
{
//
//No guest support, we don't need the guest radio button.
//
UAM_HiliteItem(gDialog, DITEM_GuestRadio, 255);
//
//If guest is not supported, then we gate the connect
//button to the username text field.
//
UAM_GateControl(gDialog, DITEM_Connect, DITEM_UserName);
}
//
//Set the initial radio for the default/current login method.
//
if (gGuestLogon)
{
UAM_SetCValue(gDialog, DITEM_GuestRadio, 1);
UAM_SetCValue(gDialog, DITEM_RegRadio, 0);
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
HideDialogItem(gDialog, x);
}
UAM_HiliteItem(gDialog, 1, 0);
}
else {
UAM_SetCValue(gDialog, DITEM_RegRadio, 1);
}
UAM_SetBulletItem(gDialog, DITEM_Password, UAM_CLRTXTPWDLEN);
UAM_SupportCmdKeys(gDialog, false);
//
//Set our custom filter function so we can handle command keys and
//manage user name maximum string length.
//
UAM_SetCustomFilterProc(gDialog, gPwdDialogFilter);
//
//If the client is not allowed to save password for this server,
//then we gray out the keychain checkbox.
//
if (UAM_KCAvailable() == false)
{
UAM_HiliteItem(gDialog, DITEM_Keychain, 255);
}
else if (gTriedKeychain)
{
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
}
//
//This flag lets up know that we've initialized our login dialog
//and that we don't need to do it again when/if we come here again.
//
gContextInited = true;
}
else {
UAM_SetText(gDialog, DITEM_UserName, inUAMArgs->Opt.pwDlg.userName);
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
//
//Hilite the password selection.
//
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
InvalRect(&gDialog->portRect);
}
do
{
ModalDialog(gDialogFilter, &theItem);
//
//Check gated controls, disable them if their text item
//counterpart has no text.
//
UAM_CheckGatedControls(gDialog);
switch(theItem)
{
case DITEM_OK:
gGuestLogon = (UAM_GetCValue(gDialog, DITEM_GuestRadio) != 0);
theError = noErr;
theLoop = false;
if (gGuestLogon)
{
inUAMArgs->Opt.pwDlg.userName[0] = 0;
inUAMArgs->Opt.pwDlg.password[0] = 0;
}
else
{
UAM_GetBulletBuffer( gDialog,
DITEM_Password,
inUAMArgs->Opt.pwDlg.password );
UAM_GetText( gDialog,
DITEM_UserName,
(Str255 *)inUAMArgs->Opt.pwDlg.userName );
}
break;
case DITEM_Cancel:
//
//VERSION 5.0: To force cancellation, we pass userCanceledError(-128)
//back to the Chooser. The old UAM would pass back dsForcedQuit which
//is the wrong value. This would cause an error dialog when cancelling.
//
theError = userCanceledError;
theLoop = false;
break;
case DITEM_GuestRadio:
//
//Set up the controls in the dialog for guest login. We don't
//need the user name and password items, so hide them from
//the user. We must explicitly enable the 'OK' button since
//it may have been disabled by the gate stuff.
//
if (UAM_IsActive(gDialog, DITEM_GuestRadio))
{
UAM_SetCValue(gDialog, DITEM_GuestRadio, 1);
UAM_SetCValue(gDialog, DITEM_RegRadio, 0);
//UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
HideDialogItem(gDialog, x);
}
//
//Now hide the keychain checkbox
//
UAM_SetCValue(gDialog, DITEM_Keychain, 0);
HideDialogItem(gDialog, DITEM_Keychain);
UAM_StopGate(gDialog, DITEM_Connect);
}
break;
case DITEM_RegRadio:
//
//Now we need all the items back that were hidden above, make
//them visible.
//
if (UAM_GetCValue(gDialog, DITEM_RegRadio) <= 0)
{
UAM_SetCValue(gDialog, DITEM_GuestRadio, 0);
UAM_SetCValue(gDialog, DITEM_RegRadio, 1);
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
ShowDialogItem(gDialog, x);
}
//
//Make the keychain item reaappear.
//
ShowDialogItem(gDialog, DITEM_Keychain);
UAM_GetText(gDialog, DITEM_UserName, &theStr);
SelectDialogItemText(gDialog, DITEM_UserName, 0, 32767);
if ((gSupportsChngPwd) && (theStr[0] != 0)) {
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 0);
}
//
//Check to see if guest is supported or not so we know if
//we need to gate the connect button.
//
if (!(gSupportedUAMs & kGuestSupported))
{
UAM_GateControl(gDialog, DITEM_Connect, DITEM_UserName);
UAM_CheckGatedControls(gDialog);
}
}
break;
case DITEM_ChangePwd:
UAM_GetBulletBuffer( gDialog,
DITEM_Password,
inUAMArgs->Opt.pwDlg.password );
UAM_GetText( gDialog,
DITEM_UserName,
(Str255 *)inUAMArgs->Opt.pwDlg.userName );
theError = UAM_ChangePwd(inUAMArgs);
switch(theError)
{
case CHNGPSWD_USER_CANCELED:
break;
case CHNGPSWD_UPDATE_KEYCHAIN:
//
//We need to re-add the keychain item with the
//correct password. Flag it by checking the box.
//
UAM_SetCValue(gDialog, DITEM_Keychain, 1);
//
//Just fall on through and handle the normal case.
//
case CHNGPSWD_NOERR:
//
//Set the password field and buffer with the new password in case
//we end back here later.
//
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
theError = noErr;
theLoop = false;
break;
default:
UAM_ReportError(theError);
//
//Because we use ParamText() we must manually force an update
//of the dialog or things won't redraw properly.
//
InvalRect(&gDialog->portRect);
break;
}
//
//Must reset our user's name since UAM_ChangePwd() uses ParamText()
//to set some strings of it's own.
//
ParamText(gServerName, NULL, NULL, NULL);
break;
case DITEM_Keychain:
UAM_ToggleControl(gDialog, DITEM_Keychain);
break;
default:
break;
}
}while(theLoop);
exit:
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ MS_UAMLogin()
// ---------------------------------------------------------------------------
// This routine does the actual logging onto the server.
OSStatus MS_UAMLogin(UAMArgs *inUAMArgs)
{
OSStatus theError = noErr;
Boolean theLoop = true;
CursHandle theCursor;
Assert_(inUAMArgs != NULL);
do
{
theCursor = GetCursor(watchCursor);
SetCursor(*theCursor);
if (gGuestLogon) {
theError = UAM_DSLoginGuest(inUAMArgs);
}
else {
theError = UAM_DSLoginMSUAM(inUAMArgs);
}
if (theError != noErr)
{
//
//For whatever reason, we couldn't log into the server, handle the most
//basic errors and try to logon again by presenting the login dialog
//again. Otherwise, exit...
//
UAM_ReportError(theError);
switch(theError)
{
case afpNTPasswordExpired:
case afpPwdExpiredErr:
UAM_CloseSession(inUAMArgs);
case afpUserNotAuth:
case afpParmErr:
case afpNTAccountDisabled:
case afpNTInvalidWorkstation:
case afpNTInvalidLogonHours:
if (MS_UAMPwdDialog(inUAMArgs) != noErr)
return(userCanceledErr);
break;
default:
theLoop = false;
theError = userCanceledErr;
break;
}
}
else
{
if ((gSupportedUAMs & kMSUAM_V2_Supported) && (!gGuestLogon))
{
//
//Check for password expiration at this point.
//
UInt32 theDaysTillExpiration = (((gExpirationTime / 60) / 60) / 24);
if (theDaysTillExpiration <= MINIMUM_DAYS_TILL_EXPIRATION)
{
//
//The password is going to expire within MINIMUM_DAYS_TILL_EXPIRATION,
//post the nofication dialog.
//
UAM_ChangePasswordNotificationDlg(theDaysTillExpiration);
}
}
if (UAM_KCAvailable())
{
//
//If the user is allowed to save their password and
//the keychain check box is checked, save the current
//credentials to the keychain.
//
if (UAM_GetCValue(gDialog, DITEM_Keychain) > 0)
{
theError = UAM_KCSavePassword(
inUAMArgs->Opt.auth.userName,
inUAMArgs->Opt.auth.password,
gServerName
);
if ((theError != noErr) && (theError != userCanceledErr))
{
if (theError == errKCDuplicateItem)
{
Int16 theResponse;
//
//A duplicate item exists, see if the user wants
//to replace it.
//
theResponse = UAM_AskQuestion(UAM_ReplaceKeyQuestion);
if (theResponse == ALRT_YES)
{
//
//The user asked us to replace the item. Try one
//more time to add the keychain item.
//
theError = UAM_KCDeleteItem(
inUAMArgs->Opt.auth.userName,
gServerName
);
if (theError == noErr)
{
theError = UAM_KCSavePassword(
inUAMArgs->Opt.auth.userName,
inUAMArgs->Opt.auth.password,
gServerName
);
if (theError != noErr)
{
//
//We errored out, nothing to do but report it.
//
UAM_ReportError(theError);
}
}
}
}
else
{
UAM_ReportError(theError);
}
//
//We do not want to pass back any keychain error codes to
//the AFP client!
//
theError = noErr;
}
}
}
theLoop = false;
}
}while(theLoop);
return(theError);
}