/*++

   Copyright    (c)    1995    Microsoft Corporation

   Module  Name:
      fcache.cxx

   Abstract:
      This module supports functions for file caching for servers

   Author:

       Murali R. Krishnan    ( MuraliK )     11-Oct-1995

   Environment:

       Win32 Apps

   Project:

       Internet Services Common  DLL

   Functions Exported:



   Revision History:
     Obtained from old inetsvcs.dll

--*/


/************************************************************
 *     Include Headers
 ************************************************************/
# include <tcpdllp.hxx>
# include <tssched.hxx>
# include "tsunami.hxx"
# include "tsvcinfo.hxx"
#ifdef JAPAN
#include <festrcnv.h>
#endif

# include "inetreg.h"

//
//  Prototypes
//

VOID
CacheScavenger(
    VOID * pContext
    );


//
//  Globals
//

//
//  The TTL to scavenge the cache and the id of the scheduled work item of the
//  next scheduled scavenge
//

DWORD g_cmsecObjectCacheTTL = (INETA_DEF_OBJECT_CACHE_TTL * 1000);
DWORD g_dwObjectCacheCookie = 0;

/************************************************************
 *    Functions
 ************************************************************/


#define FILE_DEMUX          42

BOOL
CheckOutCachedFile(
    IN     const CHAR *     pchFile,
    IN     TSVC_CACHE *     pTsvcCache,
    IN     HANDLE           hToken,
    OUT    BYTE * *         ppbData,
    OUT    DWORD *          pcbData,
    IN     BOOL             fIsAnonymous,
#ifdef JAPAN
    OUT    PCACHE_FILE_INFO pCacheFileInfo,
    IN     int              nCharset
#else
    OUT    PCACHE_FILE_INFO pCacheFileInfo
#endif
    )
/*++
    Description:

        Attempts to retrieve the passed file from the cache.  If it's not
        cached, then we read the file and add it to the cache.

    Arguments:

        pchFile - Fully qualified file to retrieve
        pTsvcCache - Cache object to charge memory against
        hToken - Impersonation token to open the file with
        pcbData - Receives pointer to first byte of data, used as handle to
            free data
        pcbSize - Size of output buffer
        pCacheFileInfo - File cache information
#ifdef JAPAN
        nCharset - Charset (if this isn't SJIS, we convert it to SJIS
            before Check-In)
#endif

    Returns:
        TRUE if successful, FALSE otherwise

    Notes:

        The file is extended by two bytes and is appended with two zero bytes,
        thus callers are guaranteed of a zero terminated text file.

--*/
{
    TS_OPEN_FILE_INFO * pFile = NULL;
    DWORD               cbLow, cbHigh;
    BYTE *              pbData = NULL;
    OVERLAPPED          Overlapped;
    BOOL                fCached = fIsAnonymous; // Non-anon will never be cached
#ifdef JAPAN
    BYTE *              pbBuff = NULL;
    int                 cbSJISSize;
#endif

    //
    //  Is the file already in the cache?
    //

    if ( !fIsAnonymous ||
         !TsCheckOutCachedBlob( *pTsvcCache,
                                pchFile,
                                FILE_DEMUX,
                                (VOID **) &pbData,
                                pcbData ))
    {
        //
        //  The file isn't in the cache so open the file and get its size
        //

        fCached = FALSE;

        if ( !(pFile = TsCreateFile( *pTsvcCache,
                                     pchFile,
                                     hToken,
                                     (fIsAnonymous ? TS_CACHING_DESIRED :
                                                     0) )) ||
             !pFile->QuerySize( &cbLow, &cbHigh ))
        {
            goto ErrorExit;
        }

        //
        //  Limit the file size to 128k
        //

        if ( cbHigh || cbLow > 131092L )
        {
            SetLastError( ERROR_NOT_SUPPORTED );
            goto ErrorExit;
        }

#ifdef JAPAN
        if ( CODE_JPN_JIS == nCharset || CODE_JPN_EUC == nCharset )
        {
            if ( !( pbBuff = pbData = (BYTE *)LocalAlloc( LPTR, cbLow ) ) )
            {
                SetLastError( ERROR_NOT_ENOUGH_MEMORY);
                goto ErrorExit;
            }
        }
        else
#endif // JAPAN
        if ( !TsAllocate( *pTsvcCache,
                          cbLow + sizeof(WCHAR),
                          (VOID **) &pbData ))
        {
            goto ErrorExit;
        }

#ifndef CHICAGO

        //
        //  Read the file data
        //

        Overlapped.Offset = 0;
        Overlapped.OffsetHigh = 0;
        Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

        if ( !Overlapped.hEvent )
            goto ErrorExit;

        if ( !ReadFile( pFile->QueryFileHandle(),
                        pbData,
                        cbLow,
                        pcbData,
                        &Overlapped ))
        {
            if ( GetLastError() != ERROR_IO_PENDING )
            {
                CloseHandle( Overlapped.hEvent );
                goto ErrorExit;
            }
        }

        if ( !GetOverlappedResult( pFile->QueryFileHandle(),
                                   &Overlapped,
                                   pcbData,
                                   TRUE ))
        {
            CloseHandle( Overlapped.hEvent );
            goto ErrorExit;
        }

        CloseHandle( Overlapped.hEvent );
#else

        //
        // No async file i/o for win95
        //

        if ( !ReadFile( pFile->QueryFileHandle(),
                        pbData,
                        cbLow,
                        pcbData,
                        NULL ))
        {
            goto ErrorExit;
        }

#endif

#ifdef JAPAN
        if ( CODE_JPN_JIS == nCharset || CODE_JPN_EUC == nCharset )
        {
            pbData = NULL;

            //
            //  get the length after conversion
            //

            cbSJISSize = UNIX_to_PC( 932,
                                     nCharset,
                                     pbBuff,
                                     *pcbData,
                                     NULL,
                                     0 );
            DBG_ASSERT( cbSJISSize <= (int)cbLow );

            if ( !TsAllocate( *pTsvcCache,
                              cbSJISSize + sizeof(WCHAR),
                              (VOID **) &pbData ))
            {
                goto ErrorExit;
            }

            //
            //  conversion
            //

            UNIX_to_PC( 932,
                        nCharset,
                        pbBuff,
                        *pcbData,
                        pbData,
                        cbSJISSize );
            *pcbData = cbLow = cbSJISSize;
        }
#endif  // JAPAN

        DBG_REQUIRE( TsCloseHandle( *pTsvcCache,
                                    pFile ));
        pFile = NULL;

        DBG_ASSERT( *pcbData <= cbLow );

        //
        //  Zero terminate the file for both ANSI and Unicode files
        //

        *((WCHAR UNALIGNED *)(pbData + cbLow)) = L'\0';

        *pcbData += sizeof(WCHAR);

        //
        //  Add this blob to the cache manager and check it out, if it fails,
        //  we just free it below
        //

        if ( fIsAnonymous &&
             TsCacheDirectoryBlob( *pTsvcCache,
                                   pchFile,
                                   FILE_DEMUX,
                                   pbData,
                                   *pcbData,
                                   TRUE ) )
        {
            fCached = TRUE;
        }
    }

    pCacheFileInfo->pbData = *ppbData = pbData;
    pCacheFileInfo->dwCacheFlags = fCached;

#ifdef JAPAN
    if ( pbBuff )
    {
        LocalFree( pbBuff );
    }
#endif

    return TRUE;

ErrorExit:
    if ( pFile )
    {
        DBG_REQUIRE( TsCloseHandle( *pTsvcCache,
                                    pFile ));
    }

#ifdef JAPAN
    if ( pbBuff )
    {
        if ( pbBuff == pbData )
        {
             pbData = NULL;
        }
        LocalFree( pbBuff );
    }
#endif

    if ( pbData )
    {
        if ( fCached )
        {
            DBG_REQUIRE( TsCheckInCachedBlob( *pTsvcCache,
                                              pbData ));
        }
        else
        {
            DBG_REQUIRE( TsFree( *pTsvcCache,
                                 pbData ));
        }
    }

    return FALSE;
}

BOOL
CheckInCachedFile(
    IN     TSVC_CACHE *     pTsvcCache,
    IN     PCACHE_FILE_INFO pCacheFileInfo
    )
/*++
    Description:

        Checks in or frees a cached file

    Arguments:

        pTsvcCache - Cache object to charge memory against
        pCacheFileInfo - Pointer to file cache information

    Returns:
        TRUE if successful, FALSE otherwise

    Notes:

--*/
{
    BOOL fRet;

    DBG_ASSERT( (pCacheFileInfo->dwCacheFlags == FALSE) || (
                 pCacheFileInfo->dwCacheFlags == TRUE) );

    //
    //  If we cached the item, check it back in to the cache, otherwise
    //  free the associated memory
    //

    if ( pCacheFileInfo->dwCacheFlags )
    {
        DBG_REQUIRE( fRet = TsCheckInCachedBlob( *pTsvcCache,
                                                 pCacheFileInfo->pbData ));
    }
    else
    {
        DBG_REQUIRE( fRet = TsFree( *pTsvcCache,
                                    pCacheFileInfo->pbData ));
    }

    return fRet;
}

BOOL
InitializeCacheScavenger(
    VOID
    )
/*++
Description:

    This function kicks off the scheduled tsunami object cache scavenger

--*/
{
    HKEY hkey;

    //
    //  Schedule a scavenger to close all of the objects that haven't been
    //  referenced in the last ttl
    //

    if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                        INETA_PARAMETERS_KEY,
                        0,
                        KEY_READ,
                        &hkey ))
    {
        g_cmsecObjectCacheTTL = ReadRegistryDword(
                                            hkey,
                                            INETA_OBJECT_CACHE_TTL,
                                            0 );

        //
        //  Don't schedule anything if the scavenger should be disabled
        //

        if ( g_cmsecObjectCacheTTL == 0xffffffff )
        {
            RegCloseKey( hkey );
            return TRUE;
        }

        //
        //  The registry setting is in seconds, convert to milliseconds
        //

        g_cmsecObjectCacheTTL *= 1000;

        //
        //  Supply the default if no value was specified
        //

        if ( !g_cmsecObjectCacheTTL )
        {
            g_cmsecObjectCacheTTL = INETA_DEF_OBJECT_CACHE_TTL * 1000;
        }

        RegCloseKey( hkey );
    }

    //
    //  Require a minimum of thirty seconds
    //

    g_cmsecObjectCacheTTL = max( g_cmsecObjectCacheTTL, 30 * 1000 );

    g_dwObjectCacheCookie = ScheduleWorkItem(
                                        (PFN_SCHED_CALLBACK) CacheScavenger,
                                        NULL,
                                        g_cmsecObjectCacheTTL );

    if ( !g_dwObjectCacheCookie )
    {
        return FALSE;
    }

    return TRUE;
}

VOID
TerminateCacheScavenger(
    VOID
    )
{
    if ( g_dwObjectCacheCookie )
    {
        RemoveWorkItem( g_dwObjectCacheCookie );
        g_dwObjectCacheCookie = 0;
    }
}

VOID
CacheScavenger(
    VOID * pContext
    )
{
    g_dwObjectCacheCookie = 0;

    //
    //  Tell tsunami to decrement the TTL on the cache items and remove
    //  anything that has timed out
    //

    TsFlushTimedOutCacheObjects();

    //
    //  Schedule ourselves again at the next TTL
    //

    g_dwObjectCacheCookie = ScheduleWorkItem(
                                        (PFN_SCHED_CALLBACK) CacheScavenger,
                                        NULL,
                                        g_cmsecObjectCacheTTL );

    if ( !g_dwObjectCacheCookie )
    {
        DBGPRINTF(( DBG_CONTEXT,
                    "[CacheScavenger] ScheduleWorkItem failed, error %d, scavenging disabled\n",
                    GetLastError() ));
    }
}

//
//  Taken from NCSA HTTP and wwwlib.
//
//  NOTE: These conform to RFC1113, which is slightly different then the Unix
//        uuencode and uudecode!
//

const int _pr2six[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

char _six2pr[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
    '0','1','2','3','4','5','6','7','8','9','+','/'
};

const int _pr2six64[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
    16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,
    40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
     0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

char _six2pr64[64] = {
    '`','!','"','#','$','%','&','\'','(',')','*','+',',',
    '-','.','/','0','1','2','3','4','5','6','7','8','9',
    ':',';','<','=','>','?','@','A','B','C','D','E','F',
    'G','H','I','J','K','L','M','N','O','P','Q','R','S',
    'T','U','V','W','X','Y','Z','[','\\',']','^','_'
};

BOOL uudecode(char   * bufcoded,
              BUFFER * pbuffdecoded,
              DWORD  * pcbDecoded,
              BOOL     fBase64
             )
{
    int nbytesdecoded;
    char *bufin = bufcoded;
    unsigned char *bufout;
    int nprbytes;
    int *pr2six = (int*)(fBase64 ? _pr2six64 : _pr2six);

    /* Strip leading whitespace. */

    while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

    /* Figure out how many characters are in the input buffer.
     * If this would decode into more bytes than would fit into
     * the output buffer, adjust the number of input bytes downwards.
     */
    bufin = bufcoded;
    while(pr2six[*(bufin++)] <= 63);
    nprbytes = bufin - bufcoded - 1;
    nbytesdecoded = ((nprbytes+3)/4) * 3;

    if ( !pbuffdecoded->Resize( nbytesdecoded + 4 ))
        return FALSE;

    if ( pcbDecoded )
        *pcbDecoded = nbytesdecoded;

    bufout = (unsigned char *) pbuffdecoded->QueryPtr();

    bufin = bufcoded;

    while (nprbytes > 0) {
        *(bufout++) =
            (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    if(nprbytes & 03) {
        if(pr2six[bufin[-2]] > 63)
            nbytesdecoded -= 2;
        else
            nbytesdecoded -= 1;
    }

    ((CHAR *)pbuffdecoded->QueryPtr())[nbytesdecoded] = '\0';

    return TRUE;
}

BOOL uuencode( BYTE *   bufin,
               DWORD    nbytes,
               BUFFER * pbuffEncoded,
               BOOL     fBase64 )
{
   unsigned char *outptr;
   unsigned int i;
   char *six2pr = fBase64 ? _six2pr64 : _six2pr;

   //
   //  Resize the buffer to 133% of the incoming data
   //

   if ( !pbuffEncoded->Resize( nbytes + ((nbytes + 3) / 3) + 4))
        return FALSE;

   outptr = (unsigned char *) pbuffEncoded->QueryPtr();

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
      *(outptr++) = six2pr[bufin[2] & 077];         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }

   *outptr = '\0';

   return TRUE;
}

/************************ End of File ***********************/