/*************************************************************************** * * File Name: rrm.c * * Copyright (C) 1993-1996 Hewlett-Packard Company. * All rights reserved. * * 11311 Chinden Blvd. * Boise, Idaho 83714 * * This is a part of the HP JetAdmin Printer Utility * * This source code is only intended as a supplement for support and * localization of HP JetAdmin by 3rd party Operating System vendors. * Modification of source code cannot be made without the express written * consent of Hewlett-Packard. * * * Description: * * Author: Name * * * Modification history: * * date initials change description * * mm-dd-yy MJB * * * * * * ***************************************************************************/ /**************************************************************************** **************************************************************************** ** ** ** File: rrm.c ** ** ** ** Description: Implementation of the Remote Resource Manager. ** ** ** ** ** **************************************************************************** ****************************************************************************/ #include "rpsyshdr.h" #include "colaintf.h" #include "rfs.h" #include "rfsext.h" #include "rrm.h" #include "rrmext.h" /* just to make sure it's up to date! */ #include "rpcxdr.h" #include "xdrext.h" #include "rrmneedx.h" typedef enum { DataDirPlease, InfoDirPlease, TempDirPlease } DirectoryTypeEnum; /* linked list structure to keep track of resources found */ typedef struct resource_tag { char Name[GLOBALNAMELENGTH]; DWORD Location; DWORD Type; RRMHANDLE MagicNumber; struct resource_tag * Next; } RRMRESOURCE, *LPRRMRESOURCE; typedef struct { char Name[GLOBALNAMELENGTH]; } FILESYSTEM; #define MAX_PRINTERS 5 typedef struct info_struct { HPERIPHERAL PrinterHandle; RFSHANDLE RFSHandle; BOOL EverEnumerated; BOOL ResourceListTypeSet; DWORD ResourceListType; RRMRESOURCE *ResourceList; BOOL FSListSet; FILESYSTEM FileSystems[MAXDEVICES]; DWORD NumFileSystems; BOOL CountEverChecked; DWORD LocalCount; void *UniqueIdPointer; int UniqueIdLength; RFSItemCount BufferSize; struct info_struct *NextPrinter; } PRINTERINFO; typedef struct { PRINTERINFO *PrinterPointer; DWORD ResourceLocation; DWORD ResourceType; } AddFileStruct; typedef struct bogus { char Name[GLOBALNAMELENGTH]; struct bogus *Next; } DirNameStruct; #define MAX_LEADER_SIZE 25 #define APP_SPECIFIC_LEADER "app-specific=" #define VERSION_LEADER "version=" #define DESCRIPTION_LEADER "description=" #define DOWNLOADER_LEADER "downloader=" #define USAGE_LEADER "usage-count=" #define ACCESSED_LEADER "last-accessed=" #define INFO_BUFFER_SIZE \ ( \ strlen(APP_SPECIFIC_LEADER) + 1 + \ (APP_SPECIFIC_LENGTH * 2) + \ strlen(VERSION_LEADER) + 1 + \ VERSIONLENGTH + \ strlen(DESCRIPTION_LEADER) + 1 + \ DESCRIPTIONLENGTH + \ strlen(DOWNLOADER_LEADER) + 1 + \ DOWNLOADERLENGTH + \ strlen(ACCESSED_LEADER) + 1 + \ LASTACCESSEDLENGTH + \ strlen(USAGE_LEADER) + 1 + 50 + \ 1 \ ) static const char Current_Dir[] = "."; static const char Parent_Dir[] = ".."; static BOOL RRMInitialized = FALSE; /* Flags whether RRMInit was called. */ static PRINTERINFO *PrinterList = NULL; /* Global list of printers */ /* Converts from RFS error codes to RRM return codes. */ static DWORD RFSStatusToRRMerror(RFSStatus Status) { switch (Status) { case RFSSuccess: return RRM_SUCCESS; break; case RFSWriteProtected: return RRM_WRITE_PROTECTED; break; case RFSNoSpaceOnDevice: return RRM_NO_SPACE_ON_DEVICE; break; default: return RRM_FAILURE; break; } /* switch (Status) */ return RRM_FAILURE; } /* RFSStatusToRRMerror */ /**************************************************************************** InitFileSystems Description: This function initializes the array of storage devices for a given printer. ****************************************************************************/ static void InitFileSystems(PRINTERINFO *PrinterPointer) { int i; if (PrinterPointer == NULL) return; for (i = 0; i < MAXDEVICES; i++) { strcpy(PrinterPointer->FileSystems[i].Name, ""); } PrinterPointer->NumFileSystems = 0; PrinterPointer->FSListSet = FALSE; return; } /**************************************************************************** InitResourceList Description: Sets the resourcelist and all associated flags to null values for a given printer. It does NOT do cleanup of a previously existing list. *****************************************************************************/ static void InitResourceList(PRINTERINFO *PrinterPointer) { if (PrinterPointer == NULL) return; PrinterPointer->EverEnumerated = FALSE; PrinterPointer->ResourceListTypeSet = FALSE; PrinterPointer->ResourceListType = 0; PrinterPointer->ResourceList = NULL; } /**************************************************************************** DestroyResourceList Description: Discards any existing resources on the resource list for a given printer and frees the memory. It then calls InitResourceList and InitFileSystems. *****************************************************************************/ static void DestroyResourceList(PRINTERINFO *PrinterPointer) { LPRRMRESOURCE TempPointer; if (PrinterPointer == NULL) return; while (PrinterPointer->ResourceList != NULL) { TempPointer = PrinterPointer->ResourceList; PrinterPointer->ResourceList = PrinterPointer->ResourceList->Next; free(TempPointer); } InitFileSystems(PrinterPointer); InitResourceList(PrinterPointer); } /* DestroyResourceList */ /* A call back function that is passed to the Remote File System routine, RFSEnumerateFileSystems. Each time RFS finds a file system, it calls this function. Each time it is called, this adds another name to the list of file systems for the particular printer. Also, the count of file systems for this printer is incremented. The printer pointer is passed as the 2nd param but don't tell anyone -- it's our little secret. */ static RFSStatus BuildFSListCallback(char FileSystemName [], LPVOID CallBackParam) { PRINTERINFO *PrinterPointer = (PRINTERINFO *)CallBackParam; /* PrinterPointer->NumFileSystems comes in here the first time at zero. Each time we add one to the list (each time this function is called), we increment PrinterPointer->NumFileSystems. */ if ((PrinterPointer->NumFileSystems < MAXDEVICES) && (strlen(FileSystemName) < (GLOBALNAMELENGTH - 1))) { strcpy(PrinterPointer->FileSystems[ PrinterPointer->NumFileSystems].Name, FileSystemName); PrinterPointer->NumFileSystems += 1; return(RFSSuccess); } return(RFSFailure); } /* BuildFSListCallback */ /* Build a list of file system name strings for a given printer. Calls RFSEnumerateFileSystems with callback BuildFSListCallback. The result is a list of file system names that are on this printer and also a count of how many are there. Does an InitFileSystems before it starts so any previous file systems are lost. */ static DWORD BuildFSList(PRINTERINFO *PrinterPointer) { RFSStatus Status; if (PrinterPointer == NULL) return RRM_FAILURE; InitFileSystems(PrinterPointer); /* Ask RFS to enumerate the file systems on this particular peripheral and store these away in the global array of FileSystems for this printer. Also count the number of these in PrinterPointer->NumFileSystems. */ Status = RFSEnumerateFileSystems(PrinterPointer->RFSHandle, BuildFSListCallback, (LPVOID)PrinterPointer); if (Status != RFSSuccess) return RRM_FAILURE; PrinterPointer->FSListSet = TRUE; return RRM_SUCCESS; } /* BuildFSList */ /* Returns a unique number every time it is called. */ static RRMHANDLE NewUniqueNumber(void) { /* Note: DO NOT let this start at zero because folks assume that this handle that we are returning is a memory address (even though it isn't) and it is common practice to check a memory address for NULL (that's zero) and assume that it's invalid if equal to NULL. Notice that the first one returned is one (1). */ static char *NextMagicNumber = 0; return ((RRMHANDLE)(++NextMagicNumber)); } /* NewUniqueNumber */ /* Find a resource of a given name, location, and type on a given printer. Return NULL if it doesn't exist. */ static LPRRMRESOURCE FindResByAttr(PRINTERINFO *PrinterPointer, LPSTR ResourceName, DWORD ResourceType, DWORD ResourceLocation) { LPRRMRESOURCE TempPtr; if (PrinterPointer == NULL) return(NULL); TempPtr = PrinterPointer->ResourceList; while (TempPtr != NULL) { if ((ResourceLocation == TempPtr->Location) && (ResourceType == TempPtr->Type) && (strcmp(ResourceName, TempPtr->Name) == 0)) { return(TempPtr); } else { TempPtr = TempPtr->Next; } } return NULL; } /* FindResByAttr */ /* Mallocs up a new resource list entry, fills in all the fields that it can (all those passed in AddFileStruct), and puts it on the resource list for the printer passed in AddFileStruct. */ static BOOL PutNewOneOnList(char *ResourceName, AddFileStruct *AFSPointer, RRMHANDLE *MagicNumPointer) { LPRRMRESOURCE TempPtr; TempPtr = (LPRRMRESOURCE)calloc(1, sizeof(RRMRESOURCE)); if (TempPtr == NULL) return FALSE; /* out of memory...ahhhhhkkkk */ /* give this new resource a unique identifier: */ TempPtr->MagicNumber = NewUniqueNumber(); /* fill in the fields */ strcpy(TempPtr->Name, ResourceName); TempPtr->Location = AFSPointer->ResourceLocation; TempPtr->Type = AFSPointer->ResourceType; /* put the new one on the front of the list */ TempPtr->Next = AFSPointer->PrinterPointer->ResourceList; AFSPointer->PrinterPointer->ResourceList = TempPtr; /* prepare our return value */ *MagicNumPointer = TempPtr->MagicNumber; return(TRUE); } /* PutNewOneOnList */ /* Creates a new DirNameStruct and copies the DirectoryName into the Name field. Puts a NULL in the Next field. Returns a pointer to the newly created DirNameStruct or NULL if unable to allocate the space for it. */ static DirNameStruct * CreateAndFillDirNameStruct(LPSTR DirectoryName) { DirNameStruct * DirNameStructPointer = NULL; DirNameStructPointer = (DirNameStruct *)calloc(1, sizeof(DirNameStruct)); if (DirNameStructPointer != NULL) { strcpy(DirNameStructPointer->Name, DirectoryName); DirNameStructPointer->Next = NULL; } return DirNameStructPointer; } /* CreateAndFillDirNameStruct */ /* Converts a resource type to a list of structures that each contain a directory name. */ static DirNameStruct * ConvertTypeToDirNameStructList(DWORD dwResourceType, DirectoryTypeEnum DirType) { DirNameStruct *DirNameStructList = NULL; if (DirType == TempDirPlease) { return CreateAndFillDirNameStruct("temp"); } switch (dwResourceType) { case RRM_FONT: { switch (DirType) { case DataDirPlease: { return CreateAndFillDirNameStruct("fonts"); } case InfoDirPlease: { return CreateAndFillDirNameStruct("fonts_info"); } default: { return NULL; } } /* switch (DirType) */ break; } /* case RRM_FONT: */ case RRM_PCL_MACRO: { DirNameStructList = CreateAndFillDirNameStruct("pcl"); if (DirNameStructList == NULL) { return NULL; } switch (DirType) { case DataDirPlease: { DirNameStructList->Next = CreateAndFillDirNameStruct("macros"); break; } case InfoDirPlease: { DirNameStructList->Next = CreateAndFillDirNameStruct("macros_info"); break; } default: { free(DirNameStructList); return NULL; } } /* switch (DirType) */ if (DirNameStructList->Next == NULL) { free(DirNameStructList); return NULL; } return DirNameStructList; break; } /* case RRM_PCL_MACRO: */ case RRM_POSTSCRIPT_RESOURCE: { switch (DirType) { case DataDirPlease: { return CreateAndFillDirNameStruct("PostScript"); } case InfoDirPlease: { return CreateAndFillDirNameStruct("PostScript_info"); } default: { return NULL; } } /* switch (DirType) */ break; } /* case RRM_POSTSCRIPT_RESOURCE: */ default: { return NULL; } } /* switch (dwResourceType) */ return NULL; } /* ConvertTypeToDirNameStructList */ /* Converts a resource location to a file system name string for the printer you are working on. */ static void ConvertLocationToFS(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD ResourceLocation, LPSTR FSName) { if (*RRMStatusPointer != RRM_SUCCESS) return; if ((PrinterPointer == NULL) || (ResourceLocation >= PrinterPointer->NumFileSystems)) { *RRMStatusPointer = RRM_FAILURE; return; } strcpy(FSName, PrinterPointer->FileSystems[ResourceLocation].Name); } /* ConvertLocationToFS */ /*************************************************************************** RRMSuperCd Description: Encapsulates all the Remote File System calls and error checking that has to occur to get into the appropriate directory on the specified peripheral. It first moves to the root directory and then changes directories to the subdirectory specified by the dwResourceType and DirType parameters. If CreateTheDirectory is TRUE, the destination directory(ies) will be created if non-existent. If CreateTheDirectory is FALSE, this function will not attempt to create the directory. If the directory doesn't exist, this function returns with an RFS error that occurs when we attempt to cd into a non-existent directory. *****************************************************************************/ static RFSStatus RRMSuperCd(RFSHANDLE RFSHandle, DWORD dwResourceType, DirectoryTypeEnum DirType, BOOL CreateTheDirectory) { DirNameStruct *DirNameStructList = NULL; DirNameStruct *CurrentPointer = NULL; RFSStatus Status; DirNameStructList = ConvertTypeToDirNameStructList(dwResourceType, DirType); if (DirNameStructList == NULL) { return RFSFailure; } Status = RFScdRoot(RFSHandle); /* move to root dir. */ /* Walk the list of directory name structures and for each one: possibly create the directory if it is selected and all is well, cd down into the directory if all is well. nuke the directory name structure as we go. */ while (DirNameStructList != NULL) { if ((Status == RFSSuccess) && (CreateTheDirectory == TRUE)) { Status = RFSCreateDirectory(RFSHandle, DirNameStructList->Name); } if (Status == RFSSuccess) { Status = RFSChangeDirectory(RFSHandle, DirNameStructList->Name); } /* nuke the list entry no matter what */ CurrentPointer = DirNameStructList; DirNameStructList = DirNameStructList->Next; free(CurrentPointer); } return Status; } /* RRMSuperCd */ /* Create the directory(ies) if non-existent cd into the directory. */ #define RRMCreateAndCd(a, b, c) RRMSuperCd(a, b, c, TRUE) /* Do not create the directory(ies) if non-existent cd into the directory. */ #define RRMcd(a, b, c) RRMSuperCd(a, b, c, FALSE) /* Buffers a string and bumps the BufferPosition according to how much was just buffered. Buffer is the beginning of the buffer (not the address of the spot to write). *BufferPositionPointer is the offset into the buffer where you should write the first byte. *BufferPositionPointer gets updated to the byte following where we just wrote. */ static void BufferAString(char *Buffer, long *BufferPositionPointer, long BufferSize, char *TheString) { long length = strlen(TheString); if (*BufferPositionPointer + length < BufferSize) { sprintf(&(Buffer[*BufferPositionPointer]), "%s", TheString); } /* Bump the pointer no matter what happens (even if too large). This is for two reasons: 1) we can use this function to determine the size of things we are going to write without actually writing it, and 2) we can determine if we have too much data to write in the buffer we have. */ *BufferPositionPointer += length; } /* BufferAString */ static void BufferOpaque(char *Buffer, long *BufferPositionPointer, long BufferSize, char *TheData, long DataLength) { long loop; int SecondLoop; char TempString[3]; /* two chars plus a null termination */ char TheChar; TempString[2] = '\0'; /* null termination */ for (loop = 0; loop < DataLength; ++loop) { for (SecondLoop = 0; SecondLoop < 2; ++SecondLoop) { /* get a nibble at a time */ /* get the most significant nibble first */ TheChar = (TheData[loop] >> ((1 - SecondLoop) * 4)) & 0xf; if ((TheChar >= 0) && (TheChar <= 9)) { TempString[SecondLoop] = '0' + TheChar; } else /* ((TheChar >= 10) && (TheChar <= 15)) */ { TempString[SecondLoop] = 'A' + (TheChar - 10); } } /* 0 to 1 */ BufferAString(Buffer, BufferPositionPointer, BufferSize, TempString); } } /* BufferOpaque */ static void BufferALong(char *Buffer, long *BufferPositionPointer, long BufferSize, long TheLong) { int loop; long TempLong; char MyBuffer[40]; /* should be big enough! */ loop = 0; if (TheLong < 0) { MyBuffer[loop] = '-'; ++loop; TheLong *= -1; } else if (TheLong == 0) { MyBuffer[loop] = '0'; ++loop; } while ((loop < 39) && (TheLong != 0)) { TempLong = (TheLong % 10) + '0'; MyBuffer[loop] = (char)TempLong; ++loop; TheLong /= 10; } MyBuffer[loop] = '\0'; BufferAString(Buffer, BufferPositionPointer, BufferSize, MyBuffer); } /* BufferALong */ /* We have a circular buffer here. As the buffer empties, we refill it with another read. We use two pointers: one (the base pointer) that is at the beginning of the area of the string that is being manipulated (this never backs up), and another (the look-ahead pointer) which goes forward a small amount from the base pointer to find the end of a string in the buffer. We allow them to advance the look-ahead pointer and we return characters at its position. We allow them to advance the base pointer which tells us that this memory in the circular buffer is now available to be overwritten by new stuff. */ /* This must be larger than any info file we expect to see (until we acually implement the circular buffer scheme). */ #define CIRC_BUFFER_SIZE 1024 typedef struct { int EndOfFileReached; long Base; long BytesInBuffer; char *Buffer; } CircBufferStruct; static void DestroyCircBuffer(CircBufferStruct *CircBufferPointer) { if (CircBufferPointer != NULL) { if (CircBufferPointer->Buffer != NULL) { free(CircBufferPointer->Buffer); } free(CircBufferPointer); } } /* DestroyCircBuffer */ static void CloseCircBuffer(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { DestroyCircBuffer(CircBufferPointer); RFSCloseFile(PrinterPointer->RFSHandle); } /* CloseCircBuffer */ static void FillErUp(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { RFSItemCount ValidDataSize; CircBufferPointer->Base = 0; if (RFSSuccess != RFSRead(PrinterPointer->RFSHandle, CIRC_BUFFER_SIZE, &ValidDataSize, CircBufferPointer->Buffer)) { CircBufferPointer->BytesInBuffer = 0; CircBufferPointer->EndOfFileReached = TRUE; return; } CircBufferPointer->BytesInBuffer = ValidDataSize; if (CircBufferPointer->BytesInBuffer < CIRC_BUFFER_SIZE) { CircBufferPointer->EndOfFileReached = TRUE; } else { CircBufferPointer->EndOfFileReached = FALSE; } } /* FillErUp */ static CircBufferStruct * InitCircBuffer(PRINTERINFO *PrinterPointer) { CircBufferStruct *CircBufferPointer; CircBufferPointer = (CircBufferStruct *) calloc(1, sizeof(CircBufferStruct)); if (CircBufferPointer != NULL) { CircBufferPointer->Buffer = (char *)calloc(1, CIRC_BUFFER_SIZE); } if ((CircBufferPointer == NULL) || (CircBufferPointer->Buffer == NULL)) { DestroyCircBuffer(CircBufferPointer); return NULL; } FillErUp(PrinterPointer, CircBufferPointer); return CircBufferPointer; } /* InitCircBuffer */ #define CharsInBuffer(p, CircBufferPointer) \ ( \ CircBufferPointer->Base < CircBufferPointer->BytesInBuffer ? \ TRUE : FALSE \ ) #define CurrentChar(CircBufferPointer) \ ( \ CircBufferPointer->Buffer[CircBufferPointer->Base] \ ) /* Advances current character to the next character. You cannot go back once you've advanced. This handles reading more data out of the file if necessary. You must call CharsInBuffer following this function to see if there are any characters left. In the case of a file read error, this acts as if the end of file has been reached and CharsInBuffer will fail when you call it following this function. */ static void AdvanceAChar(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { CircBufferPointer->Base += 1; if (CharsInBuffer(PrinterPointer, CircBufferPointer) == TRUE) { return; } /* We've exceeded the number of bytes in the buffer. If we are at the end of the file then do nothing because the user will call CharsInBuffer and it will fail, telling the user we are out of bytes. If we aren't at the end of the file, reset base to zero and read another buffer worth (or as much as we can get) from the file. */ if (CircBufferPointer->EndOfFileReached == TRUE) { return; } FillErUp(PrinterPointer, CircBufferPointer); } /* AdvanceAChar */ /* Skips spaces, tabs, newlines, form feeds, and carriage returns. Returns with current character being the character that follows these. */ static void SkipWhiteSpace(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { while ((TRUE == CharsInBuffer(PrinterPointer, CircBufferPointer)) && ((CurrentChar(CircBufferPointer) == ' ') || (CurrentChar(CircBufferPointer) == '\t') || (CurrentChar(CircBufferPointer) == '\n') || (CurrentChar(CircBufferPointer) == '\f') || (CurrentChar(CircBufferPointer) == '\r'))) { AdvanceAChar(PrinterPointer, CircBufferPointer); } } /* SkipWhiteSpace */ /* Reads characters until it reaches a newline, form feed, or carriage return. Returns with the current character on this newline, form feed, or carriage return (or end of file). */ static void SkipToEndOfLine(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { while ((TRUE == CharsInBuffer(PrinterPointer, CircBufferPointer)) && ((CurrentChar(CircBufferPointer) != '\n') && (CurrentChar(CircBufferPointer) != '\f') && (CurrentChar(CircBufferPointer) != '\r'))) { AdvanceAChar(PrinterPointer, CircBufferPointer); } } /* SkipToEndOfLine */ /* Returns the current character if it is not a space, tab, newline, form feed, or carriage return. Returns '\0' if end of file reached or if the character is a space, tab, newline, form feed, or carriage return. */ static char GetNonWhiteSpaceChar(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { char ReturnVal; if ((TRUE == CharsInBuffer(PrinterPointer, CircBufferPointer)) && ((CurrentChar(CircBufferPointer) != ' ') && (CurrentChar(CircBufferPointer) != '\t') && (CurrentChar(CircBufferPointer) != '\n') && (CurrentChar(CircBufferPointer) != '\f') && (CurrentChar(CircBufferPointer) != '\r'))) { ReturnVal = CurrentChar(CircBufferPointer); AdvanceAChar(PrinterPointer, CircBufferPointer); return ReturnVal; } else { return '\0'; } } /* GetNonWhiteSpaceChar */ static char GetNonEndChar(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer) { char ReturnVal; if ((TRUE == CharsInBuffer(PrinterPointer, CircBufferPointer)) && ((CurrentChar(CircBufferPointer) != '\n') && (CurrentChar(CircBufferPointer) != '\f') && (CurrentChar(CircBufferPointer) != '\r'))) { ReturnVal = CurrentChar(CircBufferPointer); AdvanceAChar(PrinterPointer, CircBufferPointer); return ReturnVal; } else { return '\0'; } } /* GetNonEndChar */ static void UnBufferAString(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer, long MaxLength, char *TheString) { long loop; int termination; loop = 0; termination = FALSE; do { TheString[loop] = GetNonEndChar(PrinterPointer, CircBufferPointer); if ('\0' == TheString[loop]) { termination = TRUE; } else { ++loop; } } while ((loop < (MaxLength - 1)) && (termination == FALSE)); TheString[loop] = '\0'; } /* UnBufferAString */ static void UnBufferOpaque(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer, long MaxLength, char *TheString) { long loop; int SecondLoop; int termination; char TempString[3]; loop = 0; termination = FALSE; do { UnBufferAString(PrinterPointer, CircBufferPointer, 3, TempString); if (strlen(TempString) == 2) { TheString[loop] = 0; for (SecondLoop = 0; SecondLoop < 2; ++SecondLoop) { TheString[loop] *= 16; if ((TempString[SecondLoop] >= '0') && (TempString[SecondLoop] <= '9')) { TheString[loop] += TempString[SecondLoop] - '0'; } else if ((TempString[SecondLoop] >= 'a') && (TempString[SecondLoop] <= 'f')) { TheString[loop] += TempString[SecondLoop] - 'a' + 10; } else if ((TempString[SecondLoop] >= 'A') && (TempString[SecondLoop] <= 'F')) { TheString[loop] += TempString[SecondLoop] - 'A' + 10; } } ++loop; } /* strlen is 2 */ else { termination = TRUE; } } while ((loop < MaxLength) && (termination == FALSE)); } /* UnBufferOpaque */ static void UnBufferALong(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer, long *TheLongPointer) { int termination; char TheChar; int sign; *TheLongPointer = 0; sign = 1; termination = FALSE; TheChar = GetNonEndChar(PrinterPointer, CircBufferPointer); if ('-' == TheChar) { sign = -1; TheChar = GetNonEndChar(PrinterPointer, CircBufferPointer); } do { if ('\0' == TheChar) { termination = TRUE; } else if ((TheChar >= '0') && (TheChar <= '9')) { *TheLongPointer *= 10; *TheLongPointer += TheChar - '0'; } TheChar = GetNonEndChar(PrinterPointer, CircBufferPointer); } while (termination == FALSE); *TheLongPointer *= sign; } /* UnBufferAString */ static void GetLeader(PRINTERINFO *PrinterPointer, CircBufferStruct *CircBufferPointer, char *leader) { long loop; int termination; loop = 0; termination = FALSE; do { leader[loop] = GetNonWhiteSpaceChar(PrinterPointer, CircBufferPointer); if (('=' == leader[loop]) || /* end of a leader reached */ ('\0' == leader[loop])) /* end of file reached */ { termination = TRUE; } ++loop; } while ((loop < MAX_LEADER_SIZE) && (termination == FALSE)); leader[loop] = '\0'; } /* GetLeader */ static void ReadTheInfoFile(PRINTERINFO *PrinterPointer, LPRRMINFOSTRUCT ResourceInfoPointer) { CircBufferStruct *CircBufferPointer; char leader[MAX_LEADER_SIZE + 1]; CircBufferPointer = InitCircBuffer(PrinterPointer); if (CircBufferPointer == NULL) { return; } while (TRUE == CharsInBuffer(PrinterPointer, CircBufferPointer)) { SkipWhiteSpace(PrinterPointer, CircBufferPointer); GetLeader(PrinterPointer, CircBufferPointer, leader); if (strlen(leader) != 0) { if (strcmp(leader, APP_SPECIFIC_LEADER) == 0) { UnBufferOpaque(PrinterPointer, CircBufferPointer, APP_SPECIFIC_LENGTH, ResourceInfoPointer->AppSpecificData); } else if (strcmp(leader, VERSION_LEADER) == 0) { UnBufferAString(PrinterPointer, CircBufferPointer, VERSIONLENGTH, ResourceInfoPointer->szVersion); } else if (strcmp(leader, DESCRIPTION_LEADER) == 0) { UnBufferAString(PrinterPointer, CircBufferPointer, DESCRIPTIONLENGTH, ResourceInfoPointer->szDescription); } else if (strcmp(leader, DOWNLOADER_LEADER) == 0) { UnBufferAString(PrinterPointer, CircBufferPointer, DOWNLOADERLENGTH, ResourceInfoPointer->szDownloaderName); } else if (strcmp(leader, USAGE_LEADER) == 0) { UnBufferALong(PrinterPointer, CircBufferPointer, &(ResourceInfoPointer->dwUsageCount)); } else if (strcmp(leader, ACCESSED_LEADER) == 0) { UnBufferAString(PrinterPointer, CircBufferPointer, LASTACCESSEDLENGTH, ResourceInfoPointer->szLastAccessed); } } /* if (strlen(leader) != 0) */ SkipToEndOfLine(PrinterPointer, CircBufferPointer); } /* while TRUE == CharsInBuffer */ CloseCircBuffer(PrinterPointer, CircBufferPointer); } /* ReadTheInfoFile */ /*************************************************************************** RRMWriteResourceInfoFile Description: Used by RRMAddResource to perform the actual writes to the Remote File System to record the resource information. Uses XDR format for the file. *****************************************************************************/ static DWORD RRMWriteResourceInfoFile(PRINTERINFO *PrinterPointer, LPRRMINFOSTRUCT lpResourceInfo) { long BufferPosition; RFSStatus Status; char *Buffer = NULL; DWORD ReturnCode; Buffer = (char *)calloc(1, INFO_BUFFER_SIZE); if (Buffer == NULL) return RRM_FAILURE; BufferPosition = 0; /* -------- app specific ----- */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, APP_SPECIFIC_LEADER); BufferOpaque(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->AppSpecificData, APP_SPECIFIC_LENGTH); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); /* -------- version ---------- */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, VERSION_LEADER); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->szVersion); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); /* ------ description -------- */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, DESCRIPTION_LEADER); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->szDescription); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); /* ------ downloader --------- */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, DOWNLOADER_LEADER); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->szDownloaderName); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); /* ------ usage count -------- */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, USAGE_LEADER); BufferALong(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->dwUsageCount); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); /* ------ last accessed ------ */ BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, ACCESSED_LEADER); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, lpResourceInfo->szLastAccessed); BufferAString(Buffer, &BufferPosition, INFO_BUFFER_SIZE, "\n"); Status = RFSWrite(PrinterPointer->RFSHandle, BufferPosition, Buffer); ReturnCode = RFSStatusToRRMerror(Status); free(Buffer); return ReturnCode; } /* RRMWriteResourceInfoFile */ /* Cuts this printer out of the list of printers. Calls DestroyResourceList to get rid of any memory used by resources that are in memory for this printer. Calls RFSDestroyHandle to free up any data transfer buffers that we had for this printer. */ static void NukeThisPrinter(PRINTERINFO *PrinterPointer) { /* cut the PrinterPointer out of the list */ if (PrinterPointer == PrinterList) PrinterList = PrinterList->NextPrinter; else { PRINTERINFO *TrailingPointer, *CurrentPointer; CurrentPointer = PrinterList; while ((CurrentPointer != NULL) && (CurrentPointer != PrinterPointer)) { TrailingPointer = CurrentPointer; CurrentPointer = CurrentPointer->NextPrinter; } if (CurrentPointer == NULL) /* should never be! */ return; TrailingPointer->NextPrinter = CurrentPointer->NextPrinter; } /* PrinterPointer has been cut out of the list. */ /* Nuke the whole resource list and then the printer itself. */ DestroyResourceList(PrinterPointer); RFSDestroyHandle(PrinterPointer->RFSHandle); if ((PrinterPointer->UniqueIdPointer != NULL) && (PrinterPointer->UniqueIdLength != 0)) { free(PrinterPointer->UniqueIdPointer); } free(PrinterPointer); } /* NukeThisPrinter */ /* Sets to initial values any fields in a PRINTERINFO structure. Should be called when you malloc up a new PRINTERINFO structure. */ static void ClearPrinterInfo(PRINTERINFO *PrinterPointer) { PrinterPointer->PrinterHandle = NULL; PrinterPointer->RFSHandle = NULL; InitResourceList(PrinterPointer); InitFileSystems(PrinterPointer); PrinterPointer->CountEverChecked = FALSE; PrinterPointer->LocalCount = 0; PrinterPointer->UniqueIdPointer = NULL; PrinterPointer->UniqueIdLength = 0; PrinterPointer->NextPrinter = NULL; } /* ClearPrinterInfo */ /* Look in our cache for any knowledge of a certain HPERIPHERAL. If it is in the cache, we return the printer pointer to it. If it isn't in the cache, return NULL. We have now implemented a double check on the HPERIPHERAL: 1) the HPERIPHERAL must be equal to the one in our cache, and 2) the unique id for the HPERIPHERAL must also be equal. */ static PRINTERINFO * GetPrinterPointer(HPERIPHERAL hPeripheral) { PRINTERINFO *PrinterPointer = PrinterList; while (PrinterPointer != NULL) { if ((PrinterPointer->PrinterHandle == hPeripheral) && (TRUE == RRMCompareUniqueIds(hPeripheral, PrinterPointer->UniqueIdPointer, PrinterPointer->UniqueIdLength))) { return PrinterPointer; } PrinterPointer = PrinterPointer->NextPrinter; } return NULL; } /* GetPrinterPointer */ /* Add a new HPERIPHERAL to our cache. If the number of peripherals in our cache exceeds MAX_PRINTERS then nuke a printer at random (first one we find on the list). Establish a connection with the printer. Return NULL if any of these cannot be accomplished. */ static PRINTERINFO * AddAPrinter(HPERIPHERAL hPeripheral) { PRINTERINFO *PrinterPointer; int loop; PrinterPointer = PrinterList; loop = 0; while ((loop < MAX_PRINTERS) && (PrinterPointer != NULL)) { PrinterPointer = PrinterPointer->NextPrinter; } if (loop >= MAX_PRINTERS) { /* we have too many printers so nuke one! */ /* the first one is fine with me! */ PrinterPointer = PrinterList; NukeThisPrinter(PrinterPointer); } PrinterPointer = (PRINTERINFO *)calloc(1, sizeof(PRINTERINFO)); if (PrinterPointer == NULL) return NULL; /* out of memory...oh happy day! */ ClearPrinterInfo(PrinterPointer); /* add the printer to the front of the list ('cause it's easy) */ PrinterPointer->NextPrinter = PrinterList; PrinterList = PrinterPointer; PrinterPointer->PrinterHandle = hPeripheral; PrinterPointer->RFSHandle = RFSCreateHandle(); if (PrinterPointer->RFSHandle == NULL) { /* wonderful! RFS died. */ NukeThisPrinter(PrinterPointer); return NULL; } PrinterPointer->BufferSize = RFSSetOptimumPrinter(PrinterPointer->RFSHandle, PrinterPointer->PrinterHandle); if (PrinterPointer->BufferSize == 0) { /* wonderful! RFS died. */ NukeThisPrinter(PrinterPointer); return NULL; } /* Find the unique id for the HPERIPHERAL and keep it for future comparison (in case the HPERIPHERALs get shuffled/deleted without our knowing it). We call the function once with a NULL pointer and it returns a length for the unique id. We malloc up the space and call the function again with our pointer to our malloc'ed space and it fills it in for us. */ if ((TRUE == RRMGetUniqueId(hPeripheral, NULL, &(PrinterPointer->UniqueIdLength))) && (NULL != (PrinterPointer->UniqueIdPointer = calloc(1, PrinterPointer->UniqueIdLength))) && (TRUE == RRMGetUniqueId(hPeripheral, PrinterPointer->UniqueIdPointer, &(PrinterPointer->UniqueIdLength)))) { /* everything is peachie */ return PrinterPointer; } /* something went wrong with getting the unique id */ NukeThisPrinter(PrinterPointer); return NULL; } /* AddAPrinter */ /* Call this at boot up. It simply initializes global lists to null and remembers that it has been called. */ static DWORD RRMInit(void) { PrinterList = NULL; RRMInitialized = TRUE; return RRM_SUCCESS; } /* Reads the pml object that contains the printer's count and stores it in our local copy of the count. */ static DWORD GetAndStoreCount(PRINTERINFO *PrinterPointer) { DWORD PrinterCount; if (FALSE == RRMGetTheCount(PrinterPointer->PrinterHandle, &PrinterCount)) return RRM_FAILURE; PrinterPointer->LocalCount = PrinterCount; PrinterPointer->CountEverChecked = TRUE; return RRM_SUCCESS; } /* GetAndStoreCount */ /* Changes the pml object that contains the printer's count. */ static DWORD BumpTheCount(PRINTERINFO *PrinterPointer) { if (FALSE == RRMBumpThePrinterCount(PrinterPointer->PrinterHandle)) return RRM_FAILURE; return RRM_SUCCESS; } /* A handy combination of BumpTheCount and GetAndStoreCount */ static DWORD BumpGet(PRINTERINFO *PrinterPointer) { if ((RRM_SUCCESS == BumpTheCount(PrinterPointer)) && (RRM_SUCCESS == GetAndStoreCount(PrinterPointer))) return RRM_SUCCESS; return RRM_FAILURE; } /* BumpGet */ /* Compare our local version of the printer's change count with the printer's actual change count. Return TRUE if able to obtain the printer's count and the count is the same as our local copy. Return FALSE otherwise. */ static DWORD CountsJive(PRINTERINFO *PrinterPointer) { DWORD PrinterCount; if (PrinterPointer == NULL) return RRM_FAILURE; if (PrinterPointer->CountEverChecked == FALSE) return RRM_FAILURE; if (FALSE == RRMGetTheCount(PrinterPointer->PrinterHandle, &PrinterCount)) return RRM_FAILURE; if (PrinterPointer->LocalCount != PrinterCount) return RRM_FAILURE; return RRM_SUCCESS; } /* CountsJive */ /* Given a handle to a resource and a printer pointer, find it in the cache. This makes sure that the PrinterPointer is in the list of printers then checks that PrinterPointer's list of resources for a resource with the given handle. Return resource pointer if found. Return NULL if not found. Does NOT check for cache consistency with the printer. */ static RRMRESOURCE * FindResByHandle(PRINTERINFO *PrinterPointer, RRMHANDLE hResource) { RRMRESOURCE *ResourcePointer; PRINTERINFO *TempPrinterPointer = PrinterList; while ((TempPrinterPointer != NULL) && (TempPrinterPointer != PrinterPointer)) { TempPrinterPointer = TempPrinterPointer->NextPrinter; } /* TempPrinterPointer is either NULL or equal to PrinterPointer */ if (TempPrinterPointer != NULL) { ResourcePointer = PrinterPointer->ResourceList; while (ResourcePointer != NULL) { if (ResourcePointer->MagicNumber == hResource) { return ResourcePointer; } ResourcePointer = ResourcePointer->Next; } } return NULL; } /* FindResByHandle */ /* Given a handle to a resource, see if it is in the cache. Return success if the cache is up to date and the resource is in the cache. Return failure otherwise. */ static DWORD ValidateThisHandle(RRMHANDLE hResource, RRMRESOURCE **ResourcePointerPointer, PRINTERINFO **PrinterPointerPointer) { *PrinterPointerPointer = PrinterList; while (*PrinterPointerPointer != NULL) { *ResourcePointerPointer = (*PrinterPointerPointer)->ResourceList; while (*ResourcePointerPointer != NULL) { if ((*ResourcePointerPointer)->MagicNumber == hResource) { if (RRM_SUCCESS != CountsJive(*PrinterPointerPointer)) return RRM_BAD_HANDLE; return RRM_SUCCESS; } *ResourcePointerPointer = (*ResourcePointerPointer)->Next; } *PrinterPointerPointer = (*PrinterPointerPointer)->NextPrinter; } return RRM_BAD_HANDLE; } /* ValidateThisHandle */ /* Takes a resource out of a list of resources for a printer. Frees the memory that the resource consumed. Walks the resources for the printer and when it finds the resource, it removes it from the list. Returns RRM_NO_SUCH_RESOURCE if it can't find the resource in the printer's list (should not happen) or if the resource pointer is null (shame on you :). */ static void YankItOutOfList(PRINTERINFO *PrinterPointer, RRMRESOURCE *ResourcePointer) { LPRRMRESOURCE TrailingPointer; if (ResourcePointer == NULL) return; /* nice try, Clyde */ if (ResourcePointer == PrinterPointer->ResourceList) { /* first one in the list */ PrinterPointer->ResourceList = PrinterPointer->ResourceList->Next; free(ResourcePointer); return; } /* find the pointer BEFORE the ResourcePointer */ TrailingPointer = PrinterPointer->ResourceList; while (TrailingPointer != NULL) if (TrailingPointer->Next == ResourcePointer) break; /* We have found our resource. */ else /* Otherwise keep searching. */ TrailingPointer = TrailingPointer->Next; if (TrailingPointer == NULL) { /* Resource is not in the list. */ return; } /* TrailingPointer->Next points to our resource. */ /* Remove resource from the list. */ TrailingPointer->Next = ResourcePointer->Next; free(ResourcePointer); return; } /* YankItOutOfList */ #if 0 void RRMPrintItOut(void) { RRMRESOURCE *ResourcePointer; PRINTERINFO *PrinterPointer; TCHAR szTemp[GLOBALNAMELENGTH]; PrinterPointer = PrinterList; while (PrinterPointer != NULL) { { int i; printf(TEXT("------- printer ---------\n")); printf(TEXT("PrinterHandle %x\n"), PrinterPointer->PrinterHandle); printf(TEXT("RFSHandle %x\n"), PrinterPointer->RFSHandle); printf(TEXT("EverEnumerated %d\n"), PrinterPointer->EverEnumerated); printf(TEXT("ResListTypeSet %d\n"), PrinterPointer->ResourceListTypeSet); printf(TEXT("ResourceListType %d\n"), PrinterPointer->ResourceListType); printf(TEXT("ResourceList %x\n"), PrinterPointer->ResourceList); printf(TEXT("FSListSet %d\n"), PrinterPointer->FSListSet); printf(TEXT("NumFileSystems %d\n"), PrinterPointer->NumFileSystems); for (i = 0; i < MAXDEVICES; ++i) { MBCS_TO_UNICODE(szTemp, SIZEOF_IN_CHAR(szTemp), PrinterPointer->FileSystems[i].Name) printf(TEXT("FileSystem[%d] %s\n"), i, szTemp); } printf(TEXT("CountEverChecked %d\n"), PrinterPointer->CountEverChecked); printf(TEXT("LocalCount %d\n"), PrinterPointer->LocalCount); printf(TEXT("NextPrinter %x\n"), PrinterPointer->NextPrinter); } ResourcePointer = (PrinterPointer)->ResourceList; while (ResourcePointer != NULL) { { printf(TEXT("========== Resource ==========\n")); MBCS_TO_UNICODE(szTemp, SIZEOF_IN_CHAR(szTemp), ResourcePointer->Name) printf(TEXT("ResourceName %s\n"), szTemp); printf(TEXT("ResourceLocation %d\n"), ResourcePointer->Location); printf(TEXT("ResourceType %d\n"), ResourcePointer->Type); printf(TEXT("MagicNumber %x\n"), ResourcePointer->MagicNumber); printf(TEXT("Next %x\n"), ResourcePointer->Next); } ResourcePointer = (ResourcePointer)->Next; } PrinterPointer = (PrinterPointer)->NextPrinter; } } /* RRMPrintItOut */ #endif /* Compares the type of resources that are currently in this printer's cache with the desired resource type. Does not check for consistency of the cache with the printer. */ static DWORD ListTypeIsCorrect(PRINTERINFO *PrinterPointer, DWORD dwResourceType) { if ((PrinterPointer == NULL) || (PrinterPointer->ResourceListTypeSet == FALSE)) return RRM_FAILURE; if ((PrinterPointer->ResourceListType == dwResourceType) || (PrinterPointer->ResourceListType == RRM_ALL_RESOURCES)) return RRM_SUCCESS; return RRM_FAILURE; } /* ListTypeIsCorrect */ /* A call back function that is passed to the Remote File System routine, RFSReadDirectory. This procedure is called by the RFSReadDirectory routine each time a file is found on the Remote File System. This routine adds a file to the list of resources for the given printer. The 2nd parameter is an AddFileStruct pointer. */ static RFSStatus EnumThisResCallback(char FileName [], LPVOID CallBackParam) { AddFileStruct *AFSPointer = (AddFileStruct *)CallBackParam; RRMHANDLE Bogus; /* check to see if this resource is really a directory, not a file */ if ((strcmp(FileName, Current_Dir) == 0) || (strcmp(FileName, Parent_Dir) == 0)) return(RFSSuccess); if (TRUE == PutNewOneOnList(FileName, AFSPointer, &Bogus)) return RFSSuccess; return RFSFailure; } /* EnumThisResCallback */ /* Returns immediately without doing anything if *RRMStatusPointer != RRM_SUCCESS. Enumerates the resources of a given type and puts these into the cache for this printer. Does not update the state variables regarding the type of resources in the cache -- that is left to the caller. */ static void EnumThisResType(LPDWORD RRMStatusPointer, DWORD dwResourceType, AddFileStruct *AFSPointer) { RFSStatus Status; if (*RRMStatusPointer != RRM_SUCCESS) return; AFSPointer->ResourceType = dwResourceType; if (*RRMStatusPointer != RRM_SUCCESS) return; Status = RRMcd(AFSPointer->PrinterPointer->RFSHandle, dwResourceType, DataDirPlease); if (Status != RFSSuccess) { /* if directory non-existent then just pretend all is well */ /* same for permission denied */ if ((Status == RFSPermissionDenied) || (Status == RFSNoSuchDirectory)) { return; } else { *RRMStatusPointer = RRM_FAILURE; return; } } if (RFSSuccess != RFSReadDirectory(AFSPointer->PrinterPointer->RFSHandle, EnumThisResCallback, (LPVOID)AFSPointer)) { *RRMStatusPointer = RRM_FAILURE; return; /* yes, I know this is the end of the function */ } } /* EnumThisResType */ /* Builds a full list of resources of a given type. It starts by building a new list of file systems. If follows that by enumerating all the resources of the given type on all file systems on the printer. If all goes well, the state variables get set: PrinterPointer->EverEnumerated = TRUE; PrinterPointer->ResourceListType = dwResourceType; PrinterPointer->ResourceListTypeSet = TRUE; */ static DWORD BuildResourceList(PRINTERINFO *PrinterPointer, DWORD dwResourceType) { RFSStatus Status; DWORD RRMStatus; AddFileStruct MyStruct; DWORD loop; char *FSName = NULL; if (PrinterPointer == NULL) return RRM_FAILURE; if (RRM_SUCCESS != BuildFSList(PrinterPointer)) return RRM_FAILURE; MyStruct.PrinterPointer = PrinterPointer; FSName = (char *)calloc(1, RFSMAXFILENAMELENGTH + 1); if (FSName == NULL) { return RRM_FAILURE; } /* For each file system, enumerate the specified resources. */ for (loop = 0; loop < PrinterPointer->NumFileSystems; loop++) { MyStruct.ResourceLocation = loop; RRMStatus = RRM_SUCCESS; ConvertLocationToFS(&RRMStatus, PrinterPointer, MyStruct.ResourceLocation, FSName); if (RRMStatus != RRM_SUCCESS) { free(FSName); return RRMStatus; } Status = RFSSetFileSystem(PrinterPointer->RFSHandle, FSName); if (Status != RFSSuccess) { free(FSName); return(RRM_FAILURE); } if (dwResourceType == RRM_ALL_RESOURCES) { MyStruct.ResourceType = RRM_FONT; EnumThisResType(&RRMStatus, RRM_FONT, &MyStruct); MyStruct.ResourceType = RRM_PCL_MACRO; EnumThisResType(&RRMStatus, RRM_PCL_MACRO, &MyStruct); MyStruct.ResourceType = RRM_POSTSCRIPT_RESOURCE; EnumThisResType(&RRMStatus, RRM_POSTSCRIPT_RESOURCE, &MyStruct); } else { MyStruct.ResourceType = dwResourceType; EnumThisResType(&RRMStatus, dwResourceType, &MyStruct); } if (RRMStatus != RRM_SUCCESS) { free(FSName); return RRMStatus; } } /* for all file systems */ PrinterPointer->EverEnumerated = TRUE; PrinterPointer->ResourceListType = dwResourceType; PrinterPointer->ResourceListTypeSet = TRUE; free(FSName); return RRM_SUCCESS; } /* BuildResourceList */ /* Returns immediately without doing anything if *RRMStatusPointer != RRM_SUCCESS. If RRM hasn't been initialized then initialize. Return success if already initialized or able to initialize. Return failure if unable to initialize. */ static void InitMyself(LPDWORD RRMStatusPointer) { if (*RRMStatusPointer != RRM_SUCCESS) return; if (!RRMInitialized) { /* RRMInit has never been called. */ *RRMStatusPointer = RRMInit(); } } /* InitMyself */ /* Returns immediately without doing anything if *RRMStatusPointer != RRM_SUCCESS. Look up the peripheral handle in our list of printers. If it exists, return a printer pointer to it. If it doesn't exist, call AddAPrinter to add it. If unable to add the printer return failure. */ static void GetOrAddAPrinter(LPDWORD RRMStatusPointer, PRINTERINFO **PrinterPointerPointer, HPERIPHERAL hPeripheral) { if (*RRMStatusPointer != RRM_SUCCESS) return; *PrinterPointerPointer = GetPrinterPointer(hPeripheral); if (*PrinterPointerPointer == NULL) { /* never seen this peripheral so set up a connection */ *PrinterPointerPointer = AddAPrinter(hPeripheral); if (*PrinterPointerPointer == NULL) { *RRMStatusPointer = RRM_FAILURE; } } } /* GetOrAddAPrinter */ /* Returns immediately without doing anything if *RRMStatusPointer != RRM_SUCCESS. This got changed late in the game to enumerate all resource types all of the time. We may take a hit in performance the first enumeration but after that we should be fine. This change was so that a user can enumerate one type of resource and then come back and enumerate another kind of resource without our nuking the first list of resources. I'm sorry, I didn't handle this case well at all. This fix will make this all work for that type of user and they won't even know it's happening. If the printer's count is what it was when we last enumerated then we return without doing anything else. However, if our local list is out of date, we loop up to MAX_ENUM_RETRIES number of times trying to get a good local list of resources. */ static void UpdateTheResourceList(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, HPERIPHERAL hPeripheral) { int loop; if (*RRMStatusPointer != RRM_SUCCESS) return; if ((RRM_SUCCESS != CountsJive(PrinterPointer)) || (RRM_SUCCESS != ListTypeIsCorrect(PrinterPointer, (DWORD)RRM_ALL_RESOURCES))) { /* Loop a maximum of MAX_ENUM_RETRIES trying */ /* to get an enumeration where the count is the */ /* same before and after the enumeration. */ loop = 0; do { ++loop; DestroyResourceList(PrinterPointer); *RRMStatusPointer = GetAndStoreCount(PrinterPointer); if (*RRMStatusPointer == RRM_SUCCESS) *RRMStatusPointer = BuildResourceList(PrinterPointer, (DWORD)RRM_ALL_RESOURCES); if (*RRMStatusPointer != RRM_SUCCESS) { NukeThisPrinter(PrinterPointer); return; /* bad exit */ } } while ((loop < MAX_ENUM_RETRIES) && (RRM_SUCCESS != CountsJive(PrinterPointer))); if (loop >= MAX_ENUM_RETRIES) { *RRMStatusPointer = RRM_FAILURE; } } /* either count or list type is incorrect */ } /* UpdateTheResourceList */ /* If no specific location is given, find the file system with the most available space. If a specific location is given, be sure that the specified location exists. */ static void FindAFileSystem(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD dwResourceLocation, LPDWORD FinalLocationPointer, LPSTR FileSystemName) { DWORD loop; RFSItemSize FreeSpace; RFSItemSize BlockSize; RFSItemCount TotalBlocks; RFSItemCount FreeBlocks; RFSItemCount AvailBlocks; if (*RRMStatusPointer != RRM_SUCCESS) return; if (dwResourceLocation == RRM_ANY_LOCATION) { /* Find file system with most room. */ FreeSpace = 0; for (loop = 0; loop < PrinterPointer->NumFileSystems; loop++) { ConvertLocationToFS(RRMStatusPointer, PrinterPointer, loop, FileSystemName); if (*RRMStatusPointer != RRM_SUCCESS) return; if ((RFSSuccess != RFSSetFileSystem(PrinterPointer->RFSHandle, FileSystemName)) || (RFSSuccess != RFSGetFileSystemInfo(PrinterPointer->RFSHandle, &BlockSize, &TotalBlocks, &FreeBlocks, &AvailBlocks))) { *RRMStatusPointer = RRM_FAILURE; return; } if (FreeSpace < ((BlockSize) * (AvailBlocks))) { /* got a new winner */ dwResourceLocation = loop; FreeSpace = (BlockSize) * (AvailBlocks); } /* found a larger one */ } /* for all file systems */ if (FreeSpace == 0) { *RRMStatusPointer = RRM_FAILURE; /* no room for anything! */ return; } } /* RRM_ANY_LOCATION */ /* dwResourceLocation holds the final location. */ *FinalLocationPointer = dwResourceLocation; ConvertLocationToFS(RRMStatusPointer, PrinterPointer, dwResourceLocation, FileSystemName); } /* FindAFileSystem */ /* This sets RRMStatusPointer to RRM_FAILURE if unable to create and cd into the data directory or sets it to RRM_RESOURCE_EXISTS if the file exists already in the data directory. Remember the data directory is the directory containing the actual font file (not the info directory containing the info file). */ static void ErrorIfAlreadyExists(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD dwResourceType, LPSTR szGlobalResourceName) { RFSFileType FileType; RFSItemSize SizeInBytes; RFSItemSize BlockSize; RFSItemSize SizeInBlocks; RFSFileTimesStruct Times; RFSStatus Status = RFSFailure; if (*RRMStatusPointer != RRM_SUCCESS) return; Status = RRMCreateAndCd(PrinterPointer->RFSHandle, dwResourceType, DataDirPlease); if (RFSSuccess != Status) { *RRMStatusPointer = RFSStatusToRRMerror(Status); } else if (RFSSuccess == GetFileInfo(PrinterPointer->RFSHandle, szGlobalResourceName, &FileType, &SizeInBytes, &BlockSize, &SizeInBlocks, &Times)) { /* resource already exists */ *RRMStatusPointer = RRM_RESOURCE_EXISTS; } } /* ErrorIfAlreadyExists */ /* Write the data file to the temporary directory. We'll keep it here and later move it to its final home in the data directory. It removes any file of the same global resource name in the temp directory and then writes a new one. It calls the callback function to get chunks of data to write to the printer's disk. When all is done, it returns the size of the newly created file. Two hosts can collide on the same temp file if they are downloading the same font (same global name) at the same or nearly same time. This could be a problem with two hosts writing the same file because the first one will be partially through the writing of the file when the second one nukes it. The second one will now start writing a new file and the first one may still be writing the same file nearer to the end (with garbage in the middle). If the first one tries to move the file after it has been deleted and before it is created by the second, it will fail. If it moves the newly created file, it will be corrupt. */ static void WriteTheTempFile(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD dwResourceType, LPSTR szGlobalResourceName, LPRRMGETRESOURCEDATAPROC lpfnDataProc, RFSItemSize *SizeInBytesPointer) { RFSFileType FileType; RFSItemSize BlockSize; RFSItemSize SizeInBlocks; RFSFileTimesStruct Times; RFSStatus Status = RFSFailure; BOOL EndOfResource; char *DataBuffer = NULL; DWORD ValidDataSize; if (*RRMStatusPointer != RRM_SUCCESS) return; DataBuffer = (char *)calloc(1, (size_t)PrinterPointer->BufferSize); if (DataBuffer == NULL) { *RRMStatusPointer = RRM_FAILURE; return; } Status = RRMCreateAndCd(PrinterPointer->RFSHandle, dwResourceType, TempDirPlease); if (RFSSuccess == Status) { Status = RFSRemoveFile(PrinterPointer->RFSHandle, szGlobalResourceName); } if (RFSSuccess == Status) { Status = RFSOpenFile(PrinterPointer->RFSHandle, szGlobalResourceName); } if (RFSSuccess != Status) { *RRMStatusPointer = RFSStatusToRRMerror(Status); free(DataBuffer); return; } /* File is now created, empty, and open. Write chunks of data as the callback gives us data. */ do { if ((*lpfnDataProc)(DataBuffer, PrinterPointer->BufferSize, &ValidDataSize, &EndOfResource) != TRUE) { *RRMStatusPointer = RRM_CALLBACK_TERMINATED; free(DataBuffer); return; } /* Otherwise keep writing buffers until no more data is left. */ Status = RFSWrite(PrinterPointer->RFSHandle, ValidDataSize, DataBuffer); if (RFSSuccess != Status) { RFSCloseFile(PrinterPointer->RFSHandle); RFSRemoveFile(PrinterPointer->RFSHandle, szGlobalResourceName); *RRMStatusPointer = RFSStatusToRRMerror(Status); free(DataBuffer); return; } } while (EndOfResource == FALSE); /* Close the file and then Get the file size into *SizeInBytesPointer */ if ((RFSSuccess != RFSCloseFile(PrinterPointer->RFSHandle)) || (RFSSuccess != GetFileInfo(PrinterPointer->RFSHandle, szGlobalResourceName, &FileType, SizeInBytesPointer, &BlockSize, &SizeInBlocks, &Times))) { /* Any error code from above will be more informative than the generic failure that we'll get from the close or info failure. So return it unless everything went well up to the close/info failure. */ if (*RRMStatusPointer != RRM_SUCCESS) { *RRMStatusPointer = RRM_FAILURE; } free(DataBuffer); return; } free(DataBuffer); } /* WriteTheTempFile */ /* Write the resource's info file in the info directory. This is its final resting place. It deletes any file by the same name before writing a new one. */ static void WriteTheInfoFile(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD dwResourceType, LPSTR szGlobalResourceName, LPRRMINFOSTRUCT lpResourceInfo) { RFSStatus Status = RFSFailure; if (*RRMStatusPointer != RRM_SUCCESS) return; Status = RRMCreateAndCd(PrinterPointer->RFSHandle, dwResourceType, InfoDirPlease); if (RFSSuccess == Status) { Status = RFSRemoveFile(PrinterPointer->RFSHandle, szGlobalResourceName); } if (RFSSuccess == Status) { Status = RFSOpenFile(PrinterPointer->RFSHandle, szGlobalResourceName); } if (RFSSuccess != Status) { *RRMStatusPointer = RFSStatusToRRMerror(Status); return; } *RRMStatusPointer = RRMWriteResourceInfoFile(PrinterPointer, lpResourceInfo); /* close the file regardless */ if (RFSSuccess != RFSCloseFile(PrinterPointer->RFSHandle)) { /* Any error code from above will be more informative than the generic failure that we'll get from the close failure. So return it unless everything went well up to the close failure. */ if (*RRMStatusPointer != RRM_SUCCESS) { *RRMStatusPointer = RRM_FAILURE; } return; } } /* WriteTheInfoFile */ /* This moves the data file from the temp directory to its final resting place in the data directory. It will not delete an existing font by the same name. */ static void MoveTheTempFile(LPDWORD RRMStatusPointer, PRINTERINFO *PrinterPointer, DWORD dwResourceType, LPSTR szGlobalResourceName) { RFSStatus Status = RFSFailure; if (*RRMStatusPointer != RRM_SUCCESS) return; /* WARNING: we do not want to remove a target file by the same name in the target directory before we try to move. If user A is slightly ahead of user B but they are overlapping on the writes to the temp file and they are doing the same font, A will move the temp file first (B has already checked to see if the target file exists and it didn't exist when it checked), B will write the tail end of a now new temp file with the beginning filled with zeroes!!!!!! B will then move this garbage file over the top of A's perfectly good one. We want to keep A's. */ Status = RRMCreateAndCd(PrinterPointer->RFSHandle, dwResourceType, DataDirPlease); if (RFSSuccess == Status) { Status = RFSMarkTargetDirectory(PrinterPointer->RFSHandle); } if (RFSSuccess == Status) { Status = RRMcd(PrinterPointer->RFSHandle, dwResourceType, TempDirPlease); } if (RFSSuccess == Status) { Status = RFSRename(PrinterPointer->RFSHandle, szGlobalResourceName, szGlobalResourceName, RFSTrue); /* use marked directory */ } if (RFSSuccess != Status) { *RRMStatusPointer = RFSStatusToRRMerror(Status); return; } } /* MoveTheTempFile */ /* This removes from the temp directory the resource's temp file. */ static void RemoveTheTempFile(PRINTERINFO *PrinterPointer, DWORD dwResourceType, LPSTR szGlobalResourceName) { if (RFSSuccess == RRMCreateAndCd(PrinterPointer->RFSHandle, dwResourceType, TempDirPlease)) { RFSRemoveFile(PrinterPointer->RFSHandle, szGlobalResourceName); } } /* RemoveTheTempFile */ /* Fill an RRMINFOSTRUCT with invalid values. The values filled in here are the values that the ERS says we should return if we don't know what should go there. For strings it's a null string. For DWORDs it's all ones. */ static void FillWithInvalidData(LPRRMINFOSTRUCT InfoPointer) { InfoPointer->szGlobalResourceName[0] = '\0'; InfoPointer->dwResourceType = (DWORD)~0; InfoPointer->dwResourceLocation = (DWORD)~0; memset(InfoPointer->AppSpecificData, 0, APP_SPECIFIC_LENGTH); InfoPointer->szVersion[0] = '\0'; InfoPointer->dwSize = (DWORD)~0; InfoPointer->szDescription[0] = '\0'; InfoPointer->szDownloaderName[0] = '\0'; InfoPointer->dwUsageCount = (DWORD)~0; InfoPointer->szLastAccessed[0] = '\0'; } /* FillWithInvalidData */ /* This transfers every field from the source to destination based upon the mask. If the bit in the mask is set, the field is transferred. No assumptions are made about the value being transferred or the name of the field. Therefore, you need to have the source set up with every correct field that you know and have the others set to the "invalid" value for that field. */ static void TransferSelectedInfo(LPRRMINFOSTRUCT DestinationInfoPointer, LPRRMINFOSTRUCT SourceInfoPointer, DWORD dwInfoMask) { if (dwInfoMask & RRM_GLOBAL_RESOURCE_NAME) strcpy(DestinationInfoPointer->szGlobalResourceName, SourceInfoPointer->szGlobalResourceName); if (dwInfoMask & RRM_RESOURCE_TYPE) DestinationInfoPointer->dwResourceType = SourceInfoPointer->dwResourceType; if (dwInfoMask & RRM_RESOURCE_LOCATION) DestinationInfoPointer->dwResourceLocation = SourceInfoPointer->dwResourceLocation; if (dwInfoMask & RRM_APP_SPECIFIC) memmove(DestinationInfoPointer->AppSpecificData, SourceInfoPointer->AppSpecificData, APP_SPECIFIC_LENGTH); if (dwInfoMask & RRM_VERSION) strcpy(DestinationInfoPointer->szVersion, SourceInfoPointer->szVersion); if (dwInfoMask & RRM_SIZE) DestinationInfoPointer->dwSize = SourceInfoPointer->dwSize; if (dwInfoMask & RRM_DESCRIPTION) strcpy(DestinationInfoPointer->szDescription, SourceInfoPointer->szDescription); if (dwInfoMask & RRM_DOWNLOADER_NAME) strcpy(DestinationInfoPointer->szDownloaderName, SourceInfoPointer->szDownloaderName); if (dwInfoMask & RRM_USAGE_COUNT) DestinationInfoPointer->dwUsageCount = SourceInfoPointer->dwUsageCount; if (dwInfoMask & RRM_LAST_ACCESSED) strcpy(DestinationInfoPointer->szLastAccessed, SourceInfoPointer->szLastAccessed); } /* TransferSelectedInfo */ /***************************************************************************** RRMTerminate Description: This is the last RRM routine called; it will destroy the RFS handle. *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMTerminate(void) { if (RRMInitialized) { while (PrinterList != NULL) NukeThisPrinter(PrinterList); } return RRM_SUCCESS; } /**************************************************************************** RRMEnumerateResources Description: -) Initializes the global Resource List and the global Storage Device list (i.e. the list of file systems). -) Calls RFSEnumerateFileSystems to fill in the array of file system names, which will be used subsequently in calling RFSSetFileSystem in order to traverse each file system looking for the specified resources -) Uses the ResourceType to traverse only one directory on each file system. Known Bugs: *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMEnumerateResources(HPERIPHERAL hPeripheral, DWORD dwResourceType, DWORD dwResourceLocation, LPRRMENUMPROC lpfnRRMEnumProc) { DWORD RRMStatus; BOOL CallBackStatus; LPRRMRESOURCE TempResourcePtr = NULL; PRINTERINFO *PrinterPointer = NULL; long LocalCount, loopster; RRMHANDLE *HandleArray; RRM_ENUM_CALLBACK_STRUCT *CBStructPointer; /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING You must look at the count before AND after we enumerate. Also, we must look at the DATA file and NOT the INFO file when we enumerate because this lessens race conditions (see add and delete resource functions for more poop). */ RRMStatus = RRM_SUCCESS; InitMyself(&RRMStatus); GetOrAddAPrinter(&RRMStatus, &PrinterPointer, hPeripheral); UpdateTheResourceList(&RRMStatus, PrinterPointer, hPeripheral); if (RRMStatus != RRM_SUCCESS) return RRMStatus; /* The resource list is of the correct type and is up to date. NOTE: it may contain all resources of all types. Traverse the ResourceList and pass any resource handles that make it through the filter to the call back procedure. Big time WARNING: It is possible for the call back procedure to delete the resources that we are passing them. This could easily cause us to goof with the very resource list that we are traversing. Because of this, I go through great pains to walk the list and make a local copy of all the resources that fit the filter. Then we walk this local list so no one can pull the rug out from under us. */ /* Walk the list and find all resources that are the correct type and that match the name filter. */ #define ITS_A_HIT (((dwResourceType == RRM_ALL_RESOURCES) || \ (TempResourcePtr->Type == dwResourceType)) && \ ((dwResourceLocation == RRM_ANY_LOCATION) || \ (TempResourcePtr->Location == dwResourceLocation))) TempResourcePtr = PrinterPointer->ResourceList; LocalCount = 0; while (TempResourcePtr != NULL) { if (ITS_A_HIT) { ++LocalCount; } TempResourcePtr = TempResourcePtr->Next; } if (LocalCount == 0) return RRM_SUCCESS; /* why bother with anything else? */ HandleArray = (RRMHANDLE *)calloc((size_t)LocalCount, sizeof(RRMHANDLE)); if (HandleArray == NULL) return RRM_FAILURE; /* out of memory...gasp! */ /* Walk the list a second time and fill in the array. */ TempResourcePtr = PrinterPointer->ResourceList; loopster = 0; while (TempResourcePtr != NULL) { if (ITS_A_HIT) { HandleArray[loopster] = TempResourcePtr->MagicNumber; ++loopster; } TempResourcePtr = TempResourcePtr->Next; } #undef ITS_A_HIT CBStructPointer = (RRM_ENUM_CALLBACK_STRUCT *) calloc(1, sizeof(RRM_ENUM_CALLBACK_STRUCT)); if (CBStructPointer == NULL) { free(HandleArray); return RRM_FAILURE; } CBStructPointer->GlobalName[GLOBALNAMELENGTH - 1] = '\0'; for (loopster = 0; loopster < LocalCount; ++loopster) { TempResourcePtr = FindResByHandle(PrinterPointer, HandleArray[loopster]); if (TempResourcePtr != NULL) { strncpy(CBStructPointer->GlobalName, TempResourcePtr->Name, GLOBALNAMELENGTH - 1); CBStructPointer->Type = TempResourcePtr->Type; CBStructPointer->Location = TempResourcePtr->Location; CBStructPointer->Handle = HandleArray[loopster]; CallBackStatus = (*lpfnRRMEnumProc)(CBStructPointer); if (CallBackStatus == FALSE) break; } } free(HandleArray); free(CBStructPointer); return(RRM_SUCCESS); } /* RRMEnumerateResources */ static DWORD ResourceCount; /* Used to count resources */ /* Increments the global variable ResourceCount each time it is called. RRMGetResourceCount calls RRMEnumerateResources and the callback passed to RRMEnumerateResources is this function. */ static BOOL CountResources(RRM_ENUM_CALLBACK_STRUCT *CBStructPointer) { ResourceCount++; return(TRUE); } /**************************************************************************** RRMGetResourceCount Description: Calls RRMEnumerateResources with a callback function that increments the global variable, ResourceCount. *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMGetResourceCount(HPERIPHERAL hPeripheral, DWORD dwResourceType, DWORD dwResourceLocation, DWORD * ResourcesCounted) { PRINTERINFO *PrinterPointer; DWORD RRMStatus; RRMStatus = RRM_SUCCESS; InitMyself(&RRMStatus); GetOrAddAPrinter(&RRMStatus, &PrinterPointer, hPeripheral); if (RRMStatus != RRM_SUCCESS) return RRMStatus; ResourceCount = 0; RRMStatus = RRMEnumerateResources(hPeripheral, dwResourceType, dwResourceLocation, CountResources); if (RRMStatus != RRM_SUCCESS) return RRMStatus; *ResourcesCounted = ResourceCount; return(RRM_SUCCESS); } /* RRMGetResourceCount */ /**************************************************************************** RRMAddResource Adds a resource of the type specified in the RRMINFOSTRUCT, using the name specified in the RRMINFOSTRUCT, to the location specified in the RRMINFOSTRUCT. If the location is specified as RRM_ANY_LOCATION then the resource is added to the device with the most free space and the RRMINFOSTRUCT is updated to indicate the actual location where the resource was placed. It will never overwrite an existing resource that has the same name, type, and location. If the location is specified as RRM_ANY_LOCATION then it is possible for the add to fail if the device with the most free space is also the one that has an existing resource with the same name and type. For fail-safe operation, delete any resources with the same name and type and location as those specified in the RRMINFOSTRUCT or restrict the location in the RRMINFOSTRUCT to a location that does not contain an existing resource with the same name and type. Known bugs: *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMAddResource(HPERIPHERAL hPeripheral, LPRRMINFOSTRUCT lpResourceInfo, LPRRMGETRESOURCEDATAPROC lpfnRRMGetResourceDataProc, RRMHANDLE *ResourceHandlePointer) { BOOL GottaNukeIt; RFSStatus Status; DWORD RRMStatus = RRM_SUCCESS; PRINTERINFO *PrinterPointer; LPRRMRESOURCE ResourcePointer; AddFileStruct MyStruct; char *FSName = NULL; RFSItemSize SizeInBytes; FSName = (char *)calloc(1, RFSMAXFILENAMELENGTH + 1); if (FSName == NULL) { return RRM_FAILURE; } /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING You must add and then bump the count to minimize the race condition that occurs when another host is trying to enumerate the resources while you're adding. This completely solves the race condition because they will see the count bumped after it's added and will re-enumerate. WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING You must add the info file first and then add the data file because no one looks for an info file until the data file exists. */ /* KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE There are two fields of the resource info structure that are not used at this time, but may be used in the future, so we need to put some initial values into these fields for our protection. If these fields do eventually get used by the caller of RRM, then this KLUDGE can be removed. */ lpResourceInfo->dwUsageCount = 0; strcpy(lpResourceInfo->szLastAccessed, ""); RRMStatus = RRM_SUCCESS; InitMyself(&RRMStatus); GetOrAddAPrinter(&RRMStatus, &PrinterPointer, hPeripheral); UpdateTheResourceList(&RRMStatus, PrinterPointer, hPeripheral); /* The resource list is of the correct type and is up to date. NOTE: it may contain all resources of all types -- not only the type that we are currently working with. */ FindAFileSystem(&RRMStatus, PrinterPointer, lpResourceInfo->dwResourceLocation, &(lpResourceInfo->dwResourceLocation), FSName); MyStruct.ResourceLocation = lpResourceInfo->dwResourceLocation; if (RRMStatus != RRM_SUCCESS) { free(FSName); return RRMStatus; } Status = RFSSetFileSystem(PrinterPointer->RFSHandle, FSName); if (Status != RFSSuccess) { free(FSName); return RRM_FAILURE; } /* By the time we reach this point, we know that we are accessing a file system that is either the one that was specified or is the one that has the most free space (if RRM_ANY_LOCATION was specified). MyStruct.ResourceLocation is set correctly. Prepare to write the temp file first, then read the size of the temp file, then write the info file, and finally rename the temp. file to match the data file. But first, write the data file in the temp directory. Delete the file if it exists in the temp directory. I know that this could stomp on someone else writing the same file but oh well. The other alternative is not to download because of a file that may have been left there by some process that died half way through. */ ErrorIfAlreadyExists(&RRMStatus, PrinterPointer, lpResourceInfo->dwResourceType, lpResourceInfo->szGlobalResourceName); /* If the file already exists then RRMStatus is set to RRM_RESOURCE_EXISTS. If unable to cd or create the data directory then RRMStatus is set to RRM_FAILURE. */ WriteTheTempFile(&RRMStatus, PrinterPointer, lpResourceInfo->dwResourceType, lpResourceInfo->szGlobalResourceName, lpfnRRMGetResourceDataProc, &SizeInBytes); if (RRMStatus != RRM_SUCCESS) { free(FSName); return RRMStatus; } /* Put the file size in the resource info. structure. */ lpResourceInfo->dwSize = SizeInBytes; WriteTheInfoFile(&RRMStatus, PrinterPointer, lpResourceInfo->dwResourceType, lpResourceInfo->szGlobalResourceName, lpResourceInfo); MoveTheTempFile(&RRMStatus, PrinterPointer, lpResourceInfo->dwResourceType, lpResourceInfo->szGlobalResourceName); /* just in case, let's get rid of the temp file */ RemoveTheTempFile(PrinterPointer, lpResourceInfo->dwResourceType, lpResourceInfo->szGlobalResourceName); if (RRMStatus != RRM_SUCCESS) { free(FSName); return RRMStatus; } MyStruct.PrinterPointer = PrinterPointer; MyStruct.ResourceType = lpResourceInfo->dwResourceType; /* We must check to see if this resource is already on the list, */ /* and if so, yank it off to prevent duplicates. */ ResourcePointer = FindResByAttr(PrinterPointer, lpResourceInfo->szGlobalResourceName, lpResourceInfo->dwResourceType, lpResourceInfo->dwResourceLocation); if (ResourcePointer != NULL) YankItOutOfList(PrinterPointer, ResourcePointer); if (FALSE == PutNewOneOnList(lpResourceInfo->szGlobalResourceName, &MyStruct, ResourceHandlePointer)) { free(FSName); return RRM_FAILURE; } if (RRM_SUCCESS != CountsJive(PrinterPointer)) GottaNukeIt = TRUE; /* someone else changed something */ else GottaNukeIt = FALSE; RRMStatus = BumpGet(PrinterPointer); if ((GottaNukeIt == TRUE) || (RRMStatus != RRM_SUCCESS)) NukeThisPrinter(PrinterPointer); free(FSName); return RRMStatus; /* BumpGet may have failed */ } /* RRMAddResource */ /**************************************************************************** RRMRetrieveResourceInformation Description: This function will provide detailed information about the specified resource. Based on the requested information, this routine will fill in the desired fields of the resource information structure that is passed in by the application. *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMRetrieveResourceInformation(RRMHANDLE hResource, DWORD dwInfoMask, LPRRMINFOSTRUCT lpResourceInfo) { RFSStatus Status; RRMRESOURCE *ResourcePointer = NULL; PRINTERINFO *PrinterPointer = NULL; LPRRMINFOSTRUCT TempResourceInfo = NULL; RFSFileType FileType; RFSItemSize SizeInBytes; RFSItemSize BlockSize; RFSItemSize SizeInBlocks; RFSFileTimesStruct Times; DWORD RRMStatus; if (!RRMInitialized) { /* RRMInit has never been called. */ RRMStatus = RRMInit(); if (RRMStatus != RRM_SUCCESS) return(RRM_FAILURE); } RRMStatus = ValidateThisHandle(hResource, &ResourcePointer, &PrinterPointer); if (RRMStatus != RRM_SUCCESS) return RRMStatus; TempResourceInfo = (LPRRMINFOSTRUCT) calloc(1, sizeof(RRMINFOSTRUCT)); if (TempResourceInfo == NULL) { return RRM_FAILURE; } FillWithInvalidData(TempResourceInfo); TempResourceInfo->dwResourceLocation = ResourcePointer->Location; TempResourceInfo->dwResourceType = ResourcePointer->Type; strcpy(TempResourceInfo->szGlobalResourceName, ResourcePointer->Name); if (dwInfoMask == (dwInfoMask & (RRM_GLOBAL_RESOURCE_NAME | RRM_RESOURCE_TYPE | RRM_RESOURCE_LOCATION))) { /* We already have all of the user's desired info. Let's save some time and return now! */ TransferSelectedInfo(lpResourceInfo, TempResourceInfo, dwInfoMask); free(TempResourceInfo); return RRM_SUCCESS; } /* Now we know the resource is in the ResourceList. We know some things about the resource and so we fill them in right now and fill all other fields with "I don't know" values. We will now see if the data file exists and will grab its size if so. If it doesn't exist, we have big problems. */ Status = RRMcd(PrinterPointer->RFSHandle, ResourcePointer->Type, DataDirPlease); if (Status == RFSSuccess) { Status = GetFileInfo(PrinterPointer->RFSHandle, ResourcePointer->Name, &FileType, &SizeInBytes, &BlockSize, &SizeInBlocks, &Times); } /* directory exists */ if (Status != RFSSuccess) { free(TempResourceInfo); return RRM_FAILURE; /* data dir or data file nuked */ } TempResourceInfo->dwSize = SizeInBytes; if (dwInfoMask == (dwInfoMask & (RRM_GLOBAL_RESOURCE_NAME | RRM_RESOURCE_TYPE | RRM_RESOURCE_LOCATION | RRM_SIZE))) { /* We already have all of the user's desired info. Let's save some time and return now! */ TransferSelectedInfo(lpResourceInfo, TempResourceInfo, dwInfoMask); free(TempResourceInfo); return RRM_SUCCESS; } /* The info directory may not exist. The info file may not exist (which is the case for fonts downloaded with PJL). We don't want to roll over and die if the info file or info directory doesn't exist. Also, we shouldn't die if the info file is garbage. Let's just do our best from here on out but return success regardless. */ if ((RFSSuccess == RRMcd(PrinterPointer->RFSHandle, ResourcePointer->Type, InfoDirPlease)) && (RFSSuccess == GetFileInfo(PrinterPointer->RFSHandle, ResourcePointer->Name, &FileType, &SizeInBytes, &BlockSize, &SizeInBlocks, &Times)) && (RFSSuccess == RFSOpenFile(PrinterPointer->RFSHandle, ResourcePointer->Name))) { ReadTheInfoFile(PrinterPointer, TempResourceInfo); } /* Transfer whatever we have to the caller. */ TransferSelectedInfo(lpResourceInfo, TempResourceInfo, dwInfoMask); free(TempResourceInfo); return RRM_SUCCESS; } /* RRMRetrieveResourceInformation */ /**************************************************************************** RRMDeleteResource Description: This routine performs the following steps: 1) Find the resource in the ResourceList and get the type. 2) Change to the appropriate directories and delete both the data file and the information file. 3) Remove the resource from the ResourceList. *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMDeleteResource(RRMHANDLE hResource) { DWORD RRMStatus; RRMRESOURCE *ResourcePointer; PRINTERINFO *PrinterPointer; BOOL GottaNukeIt; char *GlobalResourceName = NULL; DWORD dwResourceType; /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING You must bump the count then delete and then bump the count to minimize the race condition that occurs when another host is trying to use the resource that you're deleting. The first bump is there because it is likely that SNMP will be unable to bump the count, in which case we want to abort the deletion. Otherwise, we would delete and then be unable to bump and that would cause other users to not have the font they think they have and misprint (wrong font). Also, do a get of the count just before the bump of the count and compare this with our local copy. If they are different, someone else is doing something and we want to force a re-enumeration. This doesn't completely solve the race condition but it's the best we've got and it's not worth any extra effort to make it better. To help this condition, delete the data file first and then the info file so that if someone is enumerating and we are deleting, we try to get rid of the data file (which is what he's looking at) so that he never sees it. */ if ((!RRMInitialized) && (RRM_SUCCESS != RRMInit())) { return RRM_FAILURE; } RRMStatus = ValidateThisHandle(hResource, &ResourcePointer, &PrinterPointer); if (RRMStatus != RRM_SUCCESS) { return RRMStatus; } GlobalResourceName = (char *)calloc(1, GLOBALNAMELENGTH + 1); if (GlobalResourceName == NULL) { return RRM_FAILURE; } strcpy(GlobalResourceName, ResourcePointer->Name); dwResourceType = ResourcePointer->Type; /* Check the count and compare against our version of the count. If different, we will need to re-enumerate after this deletion is done. If the same, we are up to date and can avoid the re-enumeration. */ if (RRM_SUCCESS != CountsJive(PrinterPointer)) { GottaNukeIt = TRUE; /* someone else changed something */ } else { GottaNukeIt = FALSE; } /* If we can't bump the count, can't change into the directory, or can't remove the file, then die. */ if ((RRM_SUCCESS != BumpGet(PrinterPointer)) || (RFSSuccess != RRMcd(PrinterPointer->RFSHandle, dwResourceType, DataDirPlease)) || (RFSSuccess != RFSRemoveFile(PrinterPointer->RFSHandle, GlobalResourceName))) { free(GlobalResourceName); return RRM_FAILURE; } /* We have deleted the data file. This means that the resource is gone, regardless of whether or not we can delete the info file. NOTE NOTE NOTE: We will return RRM_SUCCESS from this point on because the resource has been deleted from our list and the disk. NOTE NOTE NOTE: We will return RRM_FAILURE if the BumpGet fails because this could throw off our local count and cause us to think we're up to date on a later access when we really aren't. */ YankItOutOfList(PrinterPointer, ResourcePointer); if (RFSSuccess == RRMcd(PrinterPointer->RFSHandle, dwResourceType, InfoDirPlease)) { RFSRemoveFile(PrinterPointer->RFSHandle, GlobalResourceName); } /* Check the count one more time. If someone goofed with things since we started our deletion, force the re-enumeration. Remember that we have possibly set GottaNukeIt above so only set but don't reset. */ if (RRM_SUCCESS != CountsJive(PrinterPointer)) { GottaNukeIt = TRUE; /* someone else changed something */ } /* We must do a BumpGet regardless of the result from CountsJive!!!!! We are bumping it to try to decrease the probability that a user sees the font-that-we're-deleting just after out first bump but tries to use it after. */ if (RRM_SUCCESS != BumpGet(PrinterPointer)) { NukeThisPrinter(PrinterPointer); free(GlobalResourceName); return RRM_FAILURE; } if (GottaNukeIt == TRUE) { NukeThisPrinter(PrinterPointer); } free(GlobalResourceName); return RRM_SUCCESS; /* the resource has been deleted */ } /* RRMDeleteResource */ /**************************************************************************** RRMRetrieveResource Description: Looks on the disk for the resource's data file. If found, it makes calls to the callback giving chunks of data of size PrinterPointer->BufferSize from the data file until the full file has been transferred. Does not need an info file for this to work. *****************************************************************************/ HPRRM_DLL_RRM_EXPORT(DWORD) RRMRetrieveResource(RRMHANDLE hResource, LPRRMACCEPTRESOURCEDATAPROC lpfnRRMAcceptResourceDataProc) { char *DataBuffer = NULL; RFSItemCount ValidDataSize; RFSStatus Status; RRMRESOURCE *ResourcePointer; PRINTERINFO *PrinterPointer; DWORD RRMStatus; if (!RRMInitialized) { /* RRMInit has never been called. */ RRMStatus = RRMInit(); if (RRMStatus != RRM_SUCCESS) return(RRM_FAILURE); } /* If the hResource is bad we return RRM_BAD_HANDLE If the count has changed for this printer, we do the same because the resource may have been nuked. */ RRMStatus = ValidateThisHandle(hResource, &ResourcePointer, &PrinterPointer); if (RRMStatus != RRM_SUCCESS) return RRMStatus; /* Now we know the resource is in the ResourceList; go get the data. */ if ((RFSSuccess != RRMcd(PrinterPointer->RFSHandle, ResourcePointer->Type, DataDirPlease)) || (RFSSuccess != RFSOpenFile(PrinterPointer->RFSHandle, ResourcePointer->Name))) { return RRM_FAILURE; } DataBuffer = (char *)calloc(1, (size_t)PrinterPointer->BufferSize); if (DataBuffer == NULL) { return RRM_FAILURE; } Status = RFSRead(PrinterPointer->RFSHandle, PrinterPointer->BufferSize, &ValidDataSize, DataBuffer); if (Status != RFSSuccess) { free(DataBuffer); return(RRM_FAILURE); } while ((Status == RFSSuccess) && (ValidDataSize > 0)) { if (FALSE == (*lpfnRRMAcceptResourceDataProc)(DataBuffer, ValidDataSize)) { free(DataBuffer); return RRM_CALLBACK_TERMINATED; } Status = RFSRead(PrinterPointer->RFSHandle, PrinterPointer->BufferSize, &ValidDataSize, DataBuffer); } if (Status != RFSSuccess) { /* Close the file regardless but use status from the loop as our error return. */ RFSCloseFile(PrinterPointer->RFSHandle); free(DataBuffer); return RRM_FAILURE; } else if (RFSSuccess != RFSCloseFile(PrinterPointer->RFSHandle)) { free(DataBuffer); return RRM_FAILURE; } free(DataBuffer); return(RRM_SUCCESS); } /* RRMRetrieveResource */