:1********************************************************************/
/**                     Microsoft LAN Manager                      **/
/**               Copyright(c) Microsoft Corp., 1987-1990          **/
/********************************************************************/

/*
 *  FILE STATUS:
 *  11/29/90  split from profile.h
 *  12/20/90  split most content off to Logon/Config CDD
 *  12/20/90  renamed from codespec.bse to pseudocd.txt
 */

/***********
PSEUDOCD.TXT
***********/

/****************************************************************************

    MODULE: PSEUDOCD.TXT

    PURPOSE: Contains outdated pseudocode which may still help
	     understand the code.

    FUNCTIONS:

    COMMENTS:

	The CDD covering this functionality is the Logon and
	Configuration CDD, stored in
	\\iceberg\uidev\slm\src\workbook\dd\shell\cdd\logoncdd.doc.
	The relevant section of the CDD is the section on "Low-level
	Profile Access Primitives".


****************************************************************************/


PSEUDOCODE

NOTE
NOTE This pseudocode was written as a model for the code, and is no
NOTE longer entirely accurate.
NOTE


Here is pseudocode for the externally-available routines in profile.cxx.
These routines are written in C++ but export a C interface.  See the
Configuration File Class CDD for information on the configuration file
object types.

/* global data structures: */
global PSECT pGlobalSection = NULL;
global char pszGlobalUserName[UNLEN+1]; // valid iff pGlobalSection != NULL

/* internal manifests */
#define DEFAULTFILE     "WINNET.INI"
#define DEFAULTSECTION  localprofile

/* functions: */


PROFILE_ERROR UserProfileRead(
	LPSTR pszUsername,
	LPSTR pszHomedir,
	BOOL  bDefault
	)
{
    CFGFILE CfgFile;
    PWLKR_SECT pSectionWalker;
    PCHAR pszSectionName;

    if (pGlobalSection) // forget current profile
    {
	delete pGlobalSection;
	pGlobalSection = NULL;
    }

    /* validate/canonicalize pszHomedir */

    if (bDefault)
    {
	/* form pathname from pszHomedir and DEFAULTFILE */
	pszSectionName = DEFAULTSECTION; // modulo C syntax
    }
    else
    {
        /* validate/canonicalize pszUsername */

        /* form filename from username */

        /* form pathname from pszHomedir and filename */

	pszSectionName = "." + pszUsername;
    }

// read configuration file
    if (CfgFile.Open(pathname)
    {
	<return error>;
    }

    if (!(pSectionWalker = new WLKR_SECT(&cfgFile)))
        return ERROR_OUT_OF_MEMORY;

// locate profile for specified user
    if (pSectionWalker->LocateSection(pszSectionName))
    {
	<return error>;
    }

// save profile for specified user
// only cuts section from memory representation of file,
//    file will not be written
    if (!(pGlobalSection = pSectionWalker->RemoveSection()))
    {
	<return error>
    }

    free pSectionWalker;

    strcpyf(pszGlobalUsername, pszUsername);

    return NO_ERROR;
}



/*
 * Returns PROFILE_NOT_READ when cached profile is for another user
 */
PROFILE_ERROR UserProfileConfirm(
	LPSTR pszUsername
	)
{
    /* validate/canonicalize pszUsername */

    if (!cpszUsername)
    {
	return (pGlobalSection)?PROFILE_NOT_READ:NO_ERROR;
    }

    if (!pGlobalSection) return PROFILE_NOT_READ; 
	// profile has not already been read
    
    if (strcmpf(pszGlobalUsername, pszUsername) return PROFILE_NOT_READ; 
	// cached profile is for a different user

    return NO_ERROR;
}




PROFILE_ERROR UserProfileQuery(
	LPSTR pszCanonDeviceName;
			     // NULL for list of devices, each
	                     // null-terminated, plus final extra null.
                             // (as in WinGetProfileString)
	LPSTR pszBuffer;     // returns UNC, alias or domain name
	USHORT usBufferSize; // length of above buffer
	PUSHORT pusAsgType;  // as ui2_asg_type
                             // ignored if pszCanonDeviceName==NULL
	PUSHORT pusResType   // as ui2_res_type
                             // ignored if pszCanonDeviceName==NULL
	)
{
    WLKR_LINE  wklineLineWalker;
    PLINE      pLine;
    char       pValue[MAX_ENTRY_LENGTH];

    if (!pGlobalSection) return PROFILE_NOT_READ; // profile not yet read

    if (wklineLineWalker.AssociateSection(pGlobalSection))
	return ERROR_OUT_OF_MEMORY;
    
    if (pszCanonDeviceName==NULL)
    {
// generate list of all device names in saved profile
    <whatever>
    }

// locate individual entry in saved profile
    switch (wklineLineWalker.LocateLine(pszCanonDeviceName))
    {
        <whatever>
    }

    if (!pLine = wklineLineWalker.QueryLine()) return ERROR_OUT_OF_MEMORY;

    switch (pLine->QueryValue(pValue,sizeof(pValue)))
    {
	<whatever>
    }

    UnbuildProfileEntry(...)

    return NO_ERROR;
}



/*
 * Also has the effect of updating the cached profile iff
 * the cached profile is for the specified user.
 *
 * The user is expected to ensure that usResType corresponds to
 * the type pf the remote resource, and that device pszCanonDeviceName
 * can be connected to a resource of that type.
 */
PROFILE_ERROR UserProfileSet(
	LPSTR pszUsername,
	LPSTR pszHomedir
	BOOL  bDefault,
	LPSTR pszCanonDeviceName;
	LPSTR pszRemoteName;
	USHORT usAsgType;    // as ui2_asg_type
	USHORT usResType     // as ui2_res_type
	)
{
    CFGFILE cfgFile;
    PWLKR_SECT pSectionWalker;
    PSECT      pSection;
    PCHAR      pszSectionName;
    WLKR_LINE  wklineLineWalker;

    /* validate/canonicalize pszHomedir */

    if (bDefault)
    {
	/* form pathname from pszHomedir and DEFAULTFILE */
	pszSectionName = DEFAULTSECTION; // modulo C syntax
    }
    else
    {
        /* validate/canonicalize pszUsername */

        /* form filename from username */

        /* form pathname from pszHomedir and filename */

	pszSectionName = "." + pszUsername;
    }

// read configuration file
    if (CfgFile.Open(pathname))
    {
	<return error>;
    }

    if (!(pSectionWalker = new WLKR_SECT(&cfgFile)))
        return ERROR_OUT_OF_MEMORY;

// locate user profile in config file
    if (pSectionWalker->LocateSection(pszSectionName))
    {
	<return error>;
    }

    if (wklineLineWalker.AssociateSection(pSection))
    {
	free pSection;
	return ERROR_OUT_OF_MEMORY;
    }
    
    switch (wklineLineWalker.LocateLine(pszCanonDeviceName))
    {
    case NO_ERROR:
	<change line, or remove line if pszRemoteName==NULL>
	break;
    case CFGERR_TargetNotFound:
	<add line, or do nothing if pszRemoteName==NULL>
	break;
    default:
	<return error>;
    }

    switch (CfgFile.Flush())
    {
    <whatever>
    }

    if ((pGlobalSection != NULL) && (!strcmpf(pszGlobalUsername, pszUsername))
    {
        delete pGlobalSection;
	pGlobalSection = RemoveSection(pSectionWalker);
    }
    else
	free pSection;

    free pSectionWalker;

    return NO_ERROR;
}



/*
   NOTE: if pszOldHomedir == NULL, creates empty new profile
	 if pszNewHomedir == NULL, deletes existing profile
	 if both are NULL, does nothing
   ALSO NOTE: UserProfileMove does not worry about whether the current
   user's home directory is changed, or whether someone else has changed
   the user's homedir.  Callers should deal with this.

   Never updates cached profile
 */
PROFILE_ERROR UserProfileMove(
	LPSTR pszUsername,
	LPSTR pszOldHomedir, // NULL to create new profile
	LPSTR pszNewHomedir  // NULL to delete existing profile
	)
{
    CFGFILE oldCfgFile;
    CFGFILE newCfgFile;
    PWLKR_SECT pSectionWalker;
    PSECT pSection;
    PCHAR pszSectionName;

    /* validate/canonicalize pszUsername */

    /* validate/canonicalize homedirs (NULL permitted) */

    /* form filename from username */

    pszSectionName = "." + pszUsername;

    if (pszOldHomedir != NULL)
    {

        /* form old pathname from pszOldHomedir and filename */

// open old config file
        if (oldCfgFile.Open(oldPathname))
        {
	    <return error>;
        }

        if (!(pSectionWalker = new WLKR_SECT(&oldCfgFile)))
            return ERROR_OUT_OF_MEMORY;

// remove old user profile, error if not found
        if (pSectionWalker->LocateSection(pszSectionName))
        {
	    <return error>
        }

        if (!(pSection = pSectionWalker->RemoveSection()))
        {
	    <error>
        }

        free pSectionWalker;
    }
    else if (pszNewHomedir != NULL)
    {
// create empty new profile
// BUGBUG be sure to free pSection
	if (!(pSection = new SECT)) return ERROR_OUT_OF_MEMORY;
	switch (pSection->SetName(pszSectionName))
	{
	<whatever>
	}
    }
    else
    {
	return NO_ERROR; // both are NULL -- do nothing
    }

    if (pszNewHomedir != NULL)
    {

        /* form new pathname from pszNewHomedir and filename */

// open new config file, create if it doesn't exist
        if (newCfgFile.Open(newPathname))
        {
	    <try to create directory>

            if (newCfgFile.Open(newPathname))
	        <return error>
        }

        if (!(pSectionWalker = new WLKR_SECT(&newCfgFile)))
            return ERROR_OUT_OF_MEMORY;

// remove profile from new cfg file if present
        switch (pSectionWalker->LocateSection(pszSectionName))
        {
        case CFGERR_TargetNotFound:
	    break;
        case NO_ERROR:
	    switch (pSectionWalker->DeleteSection()
	    {
	    <whatever>
	    }
	    break;
	default:
	    <return error>
        }

// insert profile
        switch (pSectionWalker->InsertSection(pSection))
        {
	<whatever>
        }

        free pSectionWalker;

// write new cfg file
	switch (newCfgFile.Flush())
	{
	<whatever>
	}
    }

    if (pszOldHomedir != NULL)
    {
// delete the old config file if empty
// write old cfg file
	switch (oldCfgFile.Flush())
	{
	<whatever>
	}
    }

    return NO_ERROR;
}
