/*++ Copyright (c) 1994 Microsoft Corporation Module Name: gfrapir.cxx Abstract: Contains the remote-side gopher API worker functions. In each case, the API proper validates the arguments. The worker functions contained herein just perform the requested operation with the supplied arguments. These functions are the remote side of the RPC interface. If the DLL is the abstract0 version (no RPC) then the A forms of the functions simply call the w functions Contents: wGopherFindFirst wGopherFindNext wGopherFindClose wGopherOpenFile wGopherReadFile wGopherQueryDataAvailable wGopherCloseHandle wGopherGetAttribute wGopherConnect wGopherDisconnect (GetView) Author: Richard L Firth (rfirth) 14-Oct-1994 Environment: Win32 DLL Revision History: 14-Oct-1994 rfirth Created --*/ #include #include "gfrapih.h" // manifests #define DEFAULT_REQUEST_BUFFER_LENGTH (MAX_GOPHER_SELECTOR_TEXT + GOPHER_REQUEST_TERMINATOR_LENGTH + 1) // private data char szQuery[] = "query "; // prepend to CSO searches // private prototypes PRIVATE DWORD GetView( IN LPSESSION_INFO SessionInfo, IN VIEW_TYPE ViewType, IN LPSTR Request, IN BOOL RequestIsGopherPlus, IN DWORD ResponseFlags, OUT LPVIEW_INFO* pViewInfo ); extern DWORD InbGopherLocalEndCacheWrite( IN HINTERNET hGopherFile, IN LPSTR lpszFileExtension, IN BOOL fNormal ); // functions DWORD wGopherFindFirst( IN LPCSTR lpszLocator, IN LPCSTR lpszSearchString OPTIONAL, OUT LPGOPHER_FIND_DATA lpBuffer OPTIONAL, OUT LPHINTERNET lpHandle ) /*++ Routine Description: Connects to the gopher server, sends a request to get directory information, gets the response and converts the gopher descriptor strings to GOPHER_FIND_DATA structures Arguments: lpszLocator - pointer to descriptor of information to get lpszSearchString - pointer to strings to search for if Locator is search server. This argument MUST be present if Locator is an search server lpBuffer - pointer to user-allocated buffer in which to return info lpHandle - pointer to returned handle if ERROR_SUCCESS returned Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY WSA error --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherFindFirst", "%q, %q, %#x, %#x", lpszLocator, lpszSearchString, lpBuffer, lpHandle )); DWORD gopherType; LPSTR requestPtr; DWORD requestLen; char hostName[MAX_GOPHER_HOST_NAME + 1]; DWORD hostNameLen; DWORD port; LPSTR gopherPlus; LPSESSION_INFO sessionInfo; DWORD error; HINTERNET findHandle; LPVIEW_INFO viewInfo; DWORD newRequestLength; DWORD searchStringsLength; // initialise variables in case of early exit (via goto) sessionInfo = NULL; // grab a buffer for the request string requestLen = DEFAULT_REQUEST_BUFFER_LENGTH; requestPtr = (LPSTR)ResizeBuffer(NULL, requestLen, FALSE); if (requestPtr == NULL) { DEBUG_LEAVE(ERROR_NOT_ENOUGH_MEMORY); return ERROR_NOT_ENOUGH_MEMORY; } // pull the individual fields out of the locator. Not interested in display // string hostNameLen = sizeof(hostName); if (!CrackLocator(lpszLocator, &gopherType, NULL, // DisplayString NULL, // DisplayStringLength requestPtr, // SelectorString &requestLen, // SelectorStringLength hostName, &hostNameLen, &port, &gopherPlus )) { error = ERROR_GOPHER_INVALID_LOCATOR; goto quit; } // find the session 'object'. If we don't have one describing the requested // gopher server then create one sessionInfo = FindOrCreateSession(hostName, port, &error); if (sessionInfo == NULL) { goto quit; } // if the request is gopher+ or plain gopher but we know the server is // gopher+ then we automatically promote the request to be gopher+. It // potentially makes life easier for this DLL (the server could tell us // the exact length of the response or be more discerning about errors) // and potentially gives more information to the app. Either way, the app // doesn't lose by this if (gopherPlus || IsGopherPlusSession(sessionInfo)) { gopherType |= GOPHER_TYPE_GOPHER_PLUS; } // calculate the length of the extra strings we have to add to the selector newRequestLength = requestLen; if ( IS_GOPHER_SEARCH_SERVER(gopherType)) { INET_ASSERT(lpszSearchString != NULL); INET_ASSERT(*lpszSearchString != '\0'); // add search strings length searchStringsLength = strlen(lpszSearchString); newRequestLength += searchStringsLength; if (IS_GOPHER_INDEX_SERVER(gopherType)) { newRequestLength++; // for tab } else { newRequestLength += sizeof(szQuery) - 1; } } else { searchStringsLength = 0; } // gopher+ requests have "\t+" or "\t$" at the end of the request if (IS_GOPHER_PLUS(gopherType)) { newRequestLength += 2; } // all requests terminated by "\r\n". Add 1 for string terminator newRequestLength += sizeof(GOPHER_REQUEST_TERMINATOR); // grow the buffer if necessary if (newRequestLength > DEFAULT_REQUEST_BUFFER_LENGTH) { requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, newRequestLength, FALSE); if (requestPtr == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } } // add the additional strings if (searchStringsLength != 0) { if (IS_GOPHER_INDEX_SERVER(gopherType)) { requestPtr[requestLen++] = GOPHER_FIELD_SEPARATOR; } else { memcpy (requestPtr + requestLen, szQuery, sizeof(szQuery) - 1); requestLen += sizeof(szQuery) - 1; } memcpy(&requestPtr[requestLen], lpszSearchString, searchStringsLength); requestLen += searchStringsLength; } if (IS_GOPHER_PLUS(gopherType)) { requestPtr[requestLen++] = GOPHER_FIELD_SEPARATOR; requestPtr[requestLen++] = IS_GOPHER_SEARCH_SERVER(gopherType)? '+' : '$'; } memcpy(&requestPtr[requestLen], GOPHER_REQUEST_TERMINATOR, sizeof(GOPHER_REQUEST_TERMINATOR) // don't scrub the '\0' in this case ); // selector munged; get the directory listing error = GetView(sessionInfo, ViewTypeFind, requestPtr, IS_GOPHER_PLUS(gopherType) ? TRUE : FALSE, BI_DOT_AT_END, &viewInfo ); // if no error was reported then we can return a directory entry if (error == ERROR_SUCCESS) { // if the caller supplied an output buffer then convert the first // directory entry to the API buffer format, else the caller wants // all gopher directory information returned by InternetFindNextFile() if (ARGUMENT_PRESENT(lpBuffer)) { error = GetDirEntry(viewInfo, lpBuffer); } if (error == ERROR_SUCCESS) { findHandle = viewInfo->Handle; } else { DereferenceView(viewInfo); } } quit: // dereference the session. If we have an active VIEW_INFO then the // reference count will still be > 0 if (sessionInfo != NULL) { DereferenceSession(sessionInfo); } // if we allocated a new request buffer for a large search request then // free it if (requestPtr != NULL) { requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, 0, FALSE); INET_ASSERT(requestPtr == NULL); } // set the handle value - ignored by caller if we don't return ERROR_SUCCESS *lpHandle = findHandle; DEBUG_LEAVE(error); return error; } DWORD wGopherFindNext( IN HINTERNET hFind, OUT LPGOPHER_FIND_DATA lpBuffer ) /*++ Routine Description: Remote side of GopherFindNext API. All parameters have been validated by the time this function is called, so we know that Buffer is large enough to hold all the returned data Arguments: hFind - handle of FIND_DATA, created by GopherFindFirstFile() lpBuffer - pointer to user-allocated buffer for GOPHER_FIND_DATA Return Value: DWORD Success - ERROR_SUCCESS Buffer contains next GOPHER_FIND_DATA structure Failure - ERROR_INVALID_HANDLE Can't find the VIEW_INFO corresponding to hFind ERROR_NO_MORE_FILES We have reached the end of the directory info --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherFindNext", "%#x, %#x", hFind, lpBuffer )); LPVIEW_INFO viewInfo; DWORD error; // locate the VIEW_INFO corresponding to hFind and the SESSION_INFO // that owns it viewInfo = FindViewByHandle(hFind, ViewTypeFind); if (viewInfo != NULL) { // just read out the next directory entry error = GetDirEntry(viewInfo, lpBuffer); // if the Find has been closed or this was the last entry, the following // dereference will cause the VIEW_INFO to be deleted, and if there are no // more requests outstanding on the SESSION_INFO then it too will be // deleted (via a dereference) DereferenceView(viewInfo); } else { error = ERROR_INVALID_HANDLE; } DEBUG_LEAVE(error); return error; } DWORD wGopherFindClose( IN HINTERNET hFind ) /*++ Routine Description: Causes the VIEW_INFO described by hFind to be removed from the SESSION_INFO and freed. If there are no other links to the data buffer then it too is deallocated Arguments: hFind - handle describing the VIEW_INFO to terminate Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_INVALID_HANDLE --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherFindClose", "%#x", hFind )); DWORD error; // atomically find and dereference the VIEW_INFO given the handle error = DereferenceViewByHandle(hFind, ViewTypeFind); DEBUG_LEAVE(error); return error; } DWORD wGopherOpenFile( IN LPCSTR lpszLocator, IN LPCSTR lpszView OPTIONAL, OUT LPHINTERNET lpHandle ) /*++ Routine Description: 'Opens' a gopher file by copying it locally and returning a handle to the buffer Arguments: lpszLocator - pointer to locator describing file to open lpszView - pointer to view name - identifies type of file to retrieve lpHandle - pointer to returned handle if ERROR_SUCCESS returned Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY WSA error --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherOpenFile", "%q, %q, %#x", lpszLocator, lpszView, lpHandle )); DWORD error; DWORD gopherType; LPSTR requestPtr; DWORD requestLen; char hostName[MAX_GOPHER_HOST_NAME + 1]; DWORD hostNameLen; DWORD port; LPSTR gopherPlus; LPSESSION_INFO sessionInfo; LPVIEW_INFO viewInfo; HINTERNET fileHandle; DWORD viewLen; // initialise variables in case of early exit (via goto) sessionInfo = NULL; fileHandle = NULL; // grab a buffer for the request string requestLen = MAX_GOPHER_SELECTOR_TEXT + 1; requestPtr = (LPSTR)ResizeBuffer((HLOCAL)NULL, requestLen, FALSE); if (requestPtr == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // pull the individual fields out of the locator. Not interested in display // string hostNameLen = sizeof(hostName); if (!CrackLocator(lpszLocator, &gopherType, NULL, // DisplayString NULL, // DisplayStringLength requestPtr, // SelectorString &requestLen, // SelectorStringLength hostName, &hostNameLen, &port, &gopherPlus )) { error = ERROR_GOPHER_INVALID_LOCATOR; goto quit; } // find the session 'object'. If we don't have one describing the requested // gopher server then create one sessionInfo = FindOrCreateSession(hostName, port, &error); if (sessionInfo == NULL) { goto quit; } // (see GopherFindFirstFile()). If gopher+ is requested or available then // make this a gopher+ request if (gopherPlus || IsGopherPlusSession(sessionInfo)) { gopherType |= GOPHER_TYPE_GOPHER_PLUS; // we at least need some space for "\t+" viewLen = sizeof(GOPHER_PLUS_INDICATOR) - 1; // get the amount of space required for the alternate view, if supplied if (ARGUMENT_PRESENT(lpszView)) { // the extra +1 here is for the '+' between the selector and the // alternate view string: the '\0' is handled by // sizeof(GOPHER_REQUEST_TERMINATOR) viewLen += strlen(lpszView); } } else { // the caller may have supplied an alternate view for a gopher0 even // though it is meaningless. Ensure that it is not used lpszView = NULL; viewLen = 0; } // grow the buffer if it is not large enough to hold the view etc. Note, if // this is true, then we have bust one of our internal limits, which is // unexpected to say the least. But this way, we can allow apps to present // completely bogus (so we think) parameters, and at least give them a try if ((requestLen + viewLen + sizeof(GOPHER_REQUEST_TERMINATOR)) > (MAX_GOPHER_SELECTOR_TEXT + 1)) { requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, requestLen + viewLen + sizeof(GOPHER_REQUEST_TERMINATOR), FALSE ); if (requestPtr == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } } // if this is a gopher plus request, then add the "\t+". If there is an // alternate view then it will be appended to the plus, else we just add // the line terminator if (IS_GOPHER_PLUS(gopherType)) { memcpy(&requestPtr[requestLen], GOPHER_PLUS_INDICATOR, sizeof(GOPHER_PLUS_INDICATOR) - 1 ); requestLen += sizeof(GOPHER_PLUS_INDICATOR) - 1; } // add the alternate view information, if any was supplied if (ARGUMENT_PRESENT(lpszView)) { memcpy(&requestPtr[requestLen], lpszView, viewLen); requestLen += viewLen; } // in gopher0 and gopher+ cases we must terminate the selector by CR-LF memcpy(&requestPtr[requestLen], GOPHER_REQUEST_TERMINATOR, sizeof(GOPHER_REQUEST_TERMINATOR) ); // selector munged; get the file error = GetView(sessionInfo, ViewTypeFile, requestPtr, (gopherPlus != NULL) ? TRUE : FALSE, IS_DOT_TERMINATED_REQUEST(gopherType) ? BI_DOT_AT_END : 0, &viewInfo ); if (error == ERROR_SUCCESS) { fileHandle = viewInfo->Handle; } quit: // free the request buffer if (requestPtr != NULL) { requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, 0, FALSE); INET_ASSERT(requestPtr == NULL); } // dereference the session - this may cause it to be deleted if (sessionInfo != NULL) { DereferenceSession(sessionInfo); } // set the handle value - ignored by caller if we don't return ERROR_SUCCESS *lpHandle = fileHandle; DEBUG_LEAVE(error); return error; } DWORD wGopherReadFile(IN HINTERNET hFile, OUT LPBYTE lpBuffer, IN DWORD dwBufferLength, OUT LPDWORD lpdwBytesReturned) /*++ Routine Description: Reads the next dwBufferLength bytes (or as much as is remaining) from the file identified by hFile and writes to lpBuffer Arguments: hFile - identifies file lpBuffer - place to return file data dwBufferLength - length of Buffer lpdwBytesReturned - amount of data written to Buffer Return Value: DWORD Success - ERROR_SUCCESS *lpdwBytesReturned written to lpBuffer Failure - ERROR_INVALID_HANDLE Couldn't find the VIEW_INFO corresponding to hFile --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherReadFile", "%#x, %#x, %d, %#x", hFile, lpBuffer, dwBufferLength, lpdwBytesReturned)); LPVIEW_INFO viewInfo; DWORD error; viewInfo = FindViewByHandle(hFile, ViewTypeFile); if (viewInfo != NULL) { LPBUFFER_INFO bufferInfo; INET_ASSERT(viewInfo->BufferInfo != NULL); bufferInfo = viewInfo->BufferInfo; bufferInfo->Buffer = lpBuffer; bufferInfo->BufferLength = dwBufferLength; error = ReadData(viewInfo, lpdwBytesReturned); DereferenceView(viewInfo); } else { error = ERROR_INVALID_HANDLE; } DEBUG_LEAVE(error); return error; } DWORD wGopherQueryDataAvailable( IN HINTERNET hFile, OUT LPDWORD lpdwNumberOfBytesAvailable ) /*++ Routine Description: Determines the amount of data available to be read on the socket Arguments: hFile - identifies gopher file lpdwNumberOfBytesAvailable - where number of available bytes returned Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_INVALID_HANDLE --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherQueryDataAvailable", "%#x, %#x", hFile, lpdwNumberOfBytesAvailable)); DWORD bytesAvailable = 0; LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); DWORD error; HINTERNET_HANDLE_TYPE handleType; error = RGetHandleType(lpThreadInfo->hObjectMapped, &handleType); if (error != ERROR_SUCCESS) { return (error); } if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto quit; } LPVIEW_INFO viewInfo; VIEW_TYPE viewType; // assume this is a directory list first if ((viewInfo = FindViewByHandle(hFile, ViewTypeFind)) != NULL) { viewType = ViewTypeFind; error = ERROR_SUCCESS; if (viewInfo->ViewOffset < viewInfo->BufferInfo->BufferLength) { bytesAvailable = sizeof(GOPHER_FIND_DATA); } } else if ((viewInfo = FindViewByHandle(hFile, ViewTypeFile)) != NULL) { viewType = ViewTypeFile; error = ERROR_SUCCESS; } else { error = ERROR_INVALID_HANDLE; } if ((error == ERROR_SUCCESS) && (bytesAvailable == 0)) { INET_ASSERT(viewInfo->BufferInfo != NULL); ICSocket *socket = viewInfo->BufferInfo->Socket; if (socket->IsValid()) { error = socket->DataAvailable(&bytesAvailable); } if ((error == ERROR_SUCCESS) && (viewType == ViewTypeFind) && (bytesAvailable != 0)) { bytesAvailable = sizeof(GOPHER_FIND_DATA); } } else { error = ERROR_SUCCESS; } if (viewInfo != NULL) { DereferenceView(viewInfo); } *lpdwNumberOfBytesAvailable = bytesAvailable; quit: if ((error == ERROR_SUCCESS) && !bytesAvailable) { InbGopherLocalEndCacheWrite(lpThreadInfo->hObjectMapped, ((handleType==TypeFtpFindHandleHtml) ?"htm":NULL), TRUE); } DEBUG_LEAVE(error); return error; } DWORD wGopherCloseHandle( IN HINTERNET hFile ) /*++ Routine Description: Causes the VIEW_INFO described by hFile to be removed from the SESSION_INFO and freed. If there are no other links to the data buffer then it too is deallocated Arguments: hFile - handle describing the VIEW_INFO to terminate Return Value: BOOL Success - ERROR_SUCCESS Failure - ERROR_INVALID_HANDLE --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherCloseHandle", "%#x", hFile )); DWORD error; // atomically find and dereference the VIEW_INFO given the handle error = DereferenceViewByHandle(hFile, ViewTypeFile); DEBUG_LEAVE(error); return error; } #if defined(GOPHER_ATTRIBUTE_SUPPORT) DWORD wGopherGetAttribute( IN LPCSTR lpszLocator, IN LPCSTR lpszAttribute, OUT LPBYTE lpBuffer, IN OUT LPDWORD lpdwBufferLength ) /*++ Routine Description: Retrieves the requested attribute Arguments: lpszLocator - descriptor of item for which attribute information will be retrieved lpszAttribute - the attribute name, e.g. +VIEWS lpBuffer - to receive attributes lpdwBufferLength - IN: length of lpBuffer OUT: number of bytes returned in lpBuffer Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_GOPHER_ATTRIBUTE_NOT_FOUND ERROR_GOPHER_NOT_GOPHER_PLUS ERROR_NOT_ENOUGH_MEMORY --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "wGopherGetAttribute", "%q, %q, %#x, %#x [%d]", lpszLocator, lpszAttribute, lpBuffer, lpdwBufferLength )); DWORD error; // the locator we are requested to get attributes for may have come from a // server other than that identified in the locator. We will search any // VIEW_INFO buffers we have looking for the locator. In the worst case // we won't find it and will have to send a request to the server error = SearchSessionsForAttribute((LPSTR)lpszLocator, (LPSTR)lpszAttribute, lpBuffer, lpdwBufferLength ); if (error == ERROR_GOPHER_ATTRIBUTE_NOT_FOUND) { char request[MAX_GOPHER_SELECTOR_TEXT + 1]; DWORD requestLen; char hostName[MAX_GOPHER_HOST_NAME + 1]; DWORD hostNameLen; DWORD port; LPSTR gopherPlus; LPSESSION_INFO sessionInfo; DWORD requestType; // its the worst case - we don't (or no longer) have the requested // locator/attributes. We must request them again from the server // pull the individual fields out of the locator. Not interested in display // string requestLen = sizeof(request); hostNameLen = sizeof(hostName); if (!CrackLocator(lpszLocator, &requestType, NULL, // DisplayString NULL, // DisplayStringLength request, // SelectorString &requestLen, // SelectorStringLength hostName, &hostNameLen, &port, &gopherPlus )) { error = ERROR_GOPHER_INVALID_LOCATOR; goto quit; } // if we already have a session to the server identified in the locator // then we will check if we already have the information stored in a // VIEW_INFO. If not then we must send the request for the attribute info sessionInfo = FindOrCreateSession(hostName, port, &error); if (sessionInfo != NULL) { // BUGBUG - IsGopherPlusSession needs to perform discovery if // unknown // if (IsGopherPlusSession(sessionInfo)) { if (TRUE) { LPSTR attributeRequest; // convert the request to a request for attributes that the // gopher server understands attributeRequest = MakeAttributeRequest(request, (LPSTR)lpszAttribute ); if (attributeRequest != NULL) { LPVIEW_INFO viewInfo; error = GetView(sessionInfo, ViewTypeFind, attributeRequest, TRUE, // RequestIsGopherPlus 0, &viewInfo ); // done with attribute request buffer (created by // MakeAttributeRequest) DEL(attributeRequest); // copy everything that came back to the caller's buffer // if there's enough space if (error == ERROR_SUCCESS) { DWORD amountToCopy; INET_ASSERT(viewInfo->BufferInfo->Flags & BI_RECEIVE_COMPLETE); AcquireBufferLock(viewInfo->BufferInfo); amountToCopy = viewInfo->BufferInfo->BufferLength; // if the buffer contains dot-terminated info, then // account for the dot if ((viewInfo->BufferInfo->Flags & BI_DOT_AT_END) // this *SHOULD* always be true, but just in case we // have an anomalous situation, we don't want to // return a negative value (i.e. a large DWORD value) && (amountToCopy > GOPHER_DOT_TERMINATOR_LENGTH)) { amountToCopy -= GOPHER_DOT_TERMINATOR_LENGTH; } if (amountToCopy <= *lpdwBufferLength) { LPBYTE attributeBuffer; attributeBuffer = viewInfo->BufferInfo->Buffer; INET_ASSERT(attributeBuffer != NULL); memcpy(lpBuffer, attributeBuffer, amountToCopy); } // whether we copied the data or not, indicate to the // caller how much data is available *lpdwBufferLength = amountToCopy; // we are done with the buffer. Unlock the BUFFER_INFO // and the VIEW_INFO. Both will probably be destroyed ReleaseBufferLock(viewInfo->BufferInfo); DereferenceView(viewInfo); } } else { error = ERROR_NOT_ENOUGH_MEMORY; } } else { error = ERROR_GOPHER_NOT_GOPHER_PLUS; } // dereference the session, possibly destroying it DereferenceSession(sessionInfo); } } quit: DEBUG_LEAVE(error); return error; } #endif // defined(GOPHER_ATTRIBUTE_SUPPORT) //DWORD //wGopherConnect( // IN LPCSTR lpszServerName, // IN INTERNET_PORT nServerPort, // IN LPCSTR lpszUsername, // IN LPCSTR lpszPassword, // IN DWORD dwService, // IN DWORD dwFlags, // OUT LPHINTERNET lpConnectHandle // ) ///*++ //Routine Description: // Creates a default gopher connection information 'object' from the // parameters supplied in InternetConnect() //Arguments: // lpszServerName - pointer to default gopher server // nServerPort - default configured gopher port (0 for use default of 70) // lpszUsername - our user's name // lpszPassword - and password // dwService - INTERNET_SERVICE_GOPHER // dwFlags - unused // lpConnectHandle - where we return the pointer to the 'object' //Return Value: // DWORD // Success - ERROR_SUCCESS // Failure - ERROR_NOT_ENOUGH_MEMORY //--*/ //{ // LPGOPHER_DEFAULT_CONNECT_INFO info; // DWORD error; // DEBUG_ENTER((DBG_GOPHER, // Dword, // "wGopherConnect", // "%q, %d, %q, %q, %d, %#x, %#x", // lpszServerName, // nServerPort, // lpszUsername, // lpszPassword, // dwService, // dwFlags, // lpConnectHandle // )); // UNREFERENCED_PARAMETER(dwService); // UNREFERENCED_PARAMETER(dwFlags); // info = NEW(GOPHER_DEFAULT_CONNECT_INFO); // if (info != NULL) { // error = ERROR_SUCCESS; // if (lpszServerName != NULL) { // info->HostName = NEW_STRING((LPSTR)lpszServerName); // if (info->HostName == NULL) { // error = ERROR_NOT_ENOUGH_MEMORY; // } // } // info->Port = nServerPort; // if ((lpszUsername != NULL) && (error == ERROR_SUCCESS)) { // info->UserName = NEW_STRING((LPSTR)lpszUsername); // if (info->UserName == NULL) { // error = ERROR_NOT_ENOUGH_MEMORY; // } // } // if ((lpszPassword != NULL) && (error == ERROR_NOT_ENOUGH_MEMORY)) { // info->Password = NEW_STRING((LPSTR)lpszPassword); // if (info->Password == NULL) { // error = ERROR_NOT_ENOUGH_MEMORY; // } // } // } else { // error = ERROR_NOT_ENOUGH_MEMORY; // } // if (error == ERROR_SUCCESS) { // *lpConnectHandle = (HINTERNET)info; // } else { // if (info != NULL) { // if (info->HostName != NULL) { // DEL_STRING(info->HostName); // } // if (info->UserName != NULL) { // DEL_STRING(info->UserName); // } // if (info->Password != NULL) { // DEL_STRING(info->Password); // } // DEL(info); // } // } // DEBUG_LEAVE(error); // return error; //} //DWORD //wGopherDisconnect( // IN HINTERNET hInternet // ) ///*++ //Routine Description: // Undoes the work of wGopherConnect //Arguments: // hInternet - handle to object created by wGopherConnect. Actually just a // pointer to GOPHER_DEFAULT_CONNECT_INFO structure //Return Value: // DWORD // Success - ERROR_SUCCESS // Failure - ERROR_INTERNET_INTERNAL_ERROR // hInternet does not identify a GOPHER_DEFAULT_CONNECT_INFO //--*/ //{ // LPGOPHER_DEFAULT_CONNECT_INFO info; // DWORD error; // DEBUG_ENTER((DBG_GOPHER, // Dword, // "wGopherDisconnect", // "%#x", // hInternet // )); // // BUGBUG - not expecting bogus pointer?! // info = (LPGOPHER_DEFAULT_CONNECT_INFO)hInternet; // if (info != NULL) { // if (info->HostName != NULL) { // DEL_STRING(info->HostName); // } // if (info->UserName != NULL) { // DEL_STRING(info->UserName); // } // if (info->Password != NULL) { // DEL_STRING(info->Password); // } // DEL(info); // error = ERROR_SUCCESS; // } else { // error = ERROR_INTERNET_INTERNAL_ERROR; // } // DEBUG_LEAVE(error); // return error; //} // private functions PRIVATE DWORD GetView( IN LPSESSION_INFO SessionInfo, IN VIEW_TYPE ViewType, IN LPSTR Request, IN BOOL RequestIsGopherPlus, IN DWORD ResponseFlags, OUT LPVIEW_INFO* pViewInfo ) /*++ Routine Description: Creates a 'view object'. Sends a request to the gopher server and receives the response. The response data is the view. Multiple simultaneous requests for the same data from the same server are serialized. It is possible that when this function terminates, the entire response may not have been received, but we have enough to return to the caller. In this case, a background thread may be actively receiving the remainder of the response Arguments: SessionInfo - pointer to SESSION_INFO describing the gopher server ViewType - type of view being requested, File or Find Request - gopher request string RequestIsGopherPlus - TRUE if this is a gopher+ request ResponseFlags - bit-mask describing expected response buffer pViewInfo - returned view info Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY --*/ { DEBUG_ENTER((DBG_GOPHER, Dword, "GetView", "%#x, %s, %q, %B, %#x, %#x", SessionInfo, (ViewType == ViewTypeFile) ? "File" : "Find", Request, RequestIsGopherPlus, ResponseFlags, pViewInfo )); DWORD error; LPVIEW_INFO viewInfo; BOOL viewCloned; // find the view info that contains the results of sending Request to // the server identified by the session info. If this succeeds then we // will have added another reference to the session info viewInfo = CreateView(SessionInfo, ViewType, Request, &error, &viewCloned); if (viewInfo != NULL) { LPBUFFER_INFO bufferInfo; // if this is a gopher+ request then mark it in the VIEW_INFO if (RequestIsGopherPlus) { viewInfo->Flags |= VI_GOPHER_PLUS; } bufferInfo = viewInfo->BufferInfo; INET_ASSERT(bufferInfo != NULL); // set the expected response type (dot-terminated or not.) Only really // useful if request is gopher0 file bufferInfo->Flags |= ResponseFlags; if (!viewCloned) { BOOL receiveComplete; // if this is a directory view then we need to communicate the fact // that ReceiveResponse needs to allocate a buffer if (ViewType == ViewTypeFind) { bufferInfo->Flags |= BI_BUFFER_RESPONSE; } // the view was created. We must make the request to the gopher // server error = GopherTransaction(viewInfo); // if there were multiple simultaneous requests for the same data // then we must signal the request event to restart those other // threads. If no other threads are waiting then we can dispense // with the request event // AcquireViewLock(SessionInfo, ViewType); // if (bufferInfo->RequestWaiters != 0) { // the request has completed, maybe with an error. In both // cases, we signal the event in the BUFFER_INFO to allow // any concurrent requesters of the same information to // continue // SetEvent(bufferInfo->RequestEvent); // } else { // no other concurrent waiters. The system can have an event // back // CloseHandle(bufferInfo->RequestEvent); // bufferInfo->RequestEvent = NULL; // } // ReleaseViewLock(SessionInfo, ViewType); } else { // we no longer allow file views to be cloned - each request for a // file must now make a separate connection to the server INET_ASSERT(ViewType != ViewTypeFile); // the view was cloned. If we made the request at the same time // another thread was making the same request to the server then // wait for that other thread to signal the request event. If the // request event handle is NULL then we don't have to wait // if (bufferInfo->RequestEvent != NULL) { // WAIT_FOR_SINGLE_OBJECT(bufferInfo->RequestEvent, error); // } else { // error = WAIT_OBJECT_0; // } // if RequestEvent existed when this clone was generated then we // decrement the number of waiters, now that we have access to the // data. // If the number of requesters goes to zero, we grab the view lock // for this list. If the number of requesters is still zero then // we close the event handle. // The event handle was only required for a special purpose - to // hold off multiple simultaneous requesters of the same data from // the same server. Once the data has been retrieved, there is no // need to keep the event // if (bufferInfo->RequestWaiters != 0) { // if (InterlockedDecrement(&bufferInfo->RequestWaiters) == 0) { // AcquireViewLock(SessionInfo, ViewType); // if (bufferInfo->RequestWaiters == 0) { // CloseHandle(bufferInfo->RequestEvent); // bufferInfo->RequestEvent = NULL; // } // ReleaseViewLock(SessionInfo, ViewType); // } // } } // if an error occurred then dereferencing the view should destroy it if (error != ERROR_SUCCESS) { viewInfo = DereferenceView(viewInfo); } } *pViewInfo = viewInfo; DEBUG_LEAVE(error); return error; }