Windows2003-3790/inetcore/wininet/gopher/session.cxx
2020-09-30 16:53:55 +02:00

982 lines
21 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
session.cxx
Abstract:
Contains functions for maintaining the global session list and SESSION_INFO
specific functions
Contents:
AcquireSessionLock
ReleaseSessionLock
CleanupSessions
(CleanupViewList)
FindOrCreateSession
(CreateSession)
(DestroySession)
ReferenceSession
DereferenceSession
AcquireViewLock
ReleaseViewLock
GopherTransaction
IsServerGopherPlus
IsGopherPlusSession
SearchSessionsForAttribute
Author:
Richard L Firth (rfirth) 19-Oct-1994
Environment:
Win32 DLL
Revision History:
19-Oct-1994 rfirth
Created
--*/
#include <wininetp.h>
#include "gfrapih.h"
//
// manifests
//
#define NULL_HANDLE ((HANDLE)0)
//
// private prototypes
//
PRIVATE
VOID
CleanupViewList(
IN LPSESSION_INFO SessionInfo,
IN VIEW_TYPE ViewType
);
PRIVATE
LPSESSION_INFO
CreateSession(
IN LPSTR Host,
IN DWORD Port,
OUT LPDWORD Error
);
PRIVATE
VOID
DestroySession(
IN LPSESSION_INFO SessionInfo
);
//
// data
//
PUBLIC SERIALIZED_LIST SessionList;
DEBUG_DATA(LONG, NumberOfSessions, 0);
//
// functions
//
VOID
AcquireSessionLock(
VOID
)
/*++
Routine Description:
Acquires the SESSION_INFO list lock
Arguments:
None.
Return Value:
None.
--*/
{
LockSerializedList(&SessionList);
}
VOID
ReleaseSessionLock(
VOID
)
/*++
Routine Description:
Releases the SESSION_INFO list lock
Arguments:
None.
Return Value:
None.
--*/
{
UnlockSerializedList(&SessionList);
}
VOID
CleanupSessions(
VOID
)
/*++
Routine Description:
Tries to Remove all SESSION_INFOs from the session list, and terminate all
active operations
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_GOPHER,
None,
"CleanupSessions",
NULL
));
while (1) {
LPSESSION_INFO sessionInfo;
//
// find the next SESSION_INFO to delete. Because we may cause the entry
// currently at the head of the list to be deleted during this loop, we
// must walk the list each time. We may also end up with a list of items
// that are marked for delete, but cannot be deleted until the threads
// that own them complete their current operations
//
AcquireSessionLock();
for (sessionInfo = (LPSESSION_INFO)HeadOfSerializedList(&SessionList);
(sessionInfo != (LPSESSION_INFO)&SessionList.List.Flink)
&& !(sessionInfo->Flags & SI_CLEANUP);
sessionInfo = (LPSESSION_INFO)sessionInfo->List.Flink) {
//
// empty loop
//
}
if (sessionInfo == (LPSESSION_INFO)&SessionList.List.Flink) {
DEBUG_PRINT(SESSION,
INFO,
("end of list\n"
));
ReleaseSessionLock();
break;
}
//
// mark this SESSION_INFO as being cleaned up, in case it does not get
// removed from the list (some other thread is accessing it)
//
sessionInfo->Flags |= SI_CLEANUP;
//
// increment the reference count so that we can release the list lock
//
ReferenceSession(sessionInfo);
ReleaseSessionLock();
//
// now we have a pointer to a SESSION_INFO that cannot be deleted until
// after we have dereferenced it. Dereference any items in the Find and
// File lists. Note that had we not referenced the SESSION_INFO above,
// it might have gotten deleted after cleaning up the Find list, and we
// would be in danger of passing a bogus pointer to the second cleanup
// view list call below
//
CleanupViewList(sessionInfo, ViewTypeFind);
CleanupViewList(sessionInfo, ViewTypeFile);
//
// finally, dereference the session. This may cause it to be deleted,
// and for the list to be changed
//
DereferenceSession(sessionInfo);
}
DEBUG_LEAVE(0);
}
PRIVATE
VOID
CleanupViewList(
IN LPSESSION_INFO SessionInfo,
IN VIEW_TYPE ViewType
)
/*++
Routine Description:
Cleans up a VIEW_INFO list on a SESSION_INFO
Arguments:
SessionInfo - pointer to SESSION_INFO we are cleaning up
ViewType - identifies which VIEW_INFO list to clean up
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_GOPHER,
None,
"CleanupViewList",
"%x, %x",
SessionInfo,
ViewType
));
//
// walk this VIEW_INFO list trying to delete everything by dereferencing
//
while (1) {
LPSERIALIZED_LIST viewList;
LPVIEW_INFO viewInfo;
HINTERNET handle;
AcquireViewLock(SessionInfo, ViewType);
viewList = &SessionInfo->FindList;
for (viewInfo = (LPVIEW_INFO)HeadOfSerializedList(viewList);
(viewInfo != (LPVIEW_INFO)&viewList->List.Flink)
&& !(viewInfo->Flags & VI_CLEANUP);
viewInfo = (LPVIEW_INFO)viewInfo->List.Flink) {
//
// empty loop
//
}
if (viewInfo == (LPVIEW_INFO)&viewList->List.Flink) {
DEBUG_PRINT(SESSION,
INFO,
("end of list\n"
));
ReleaseViewLock(SessionInfo, ViewType);
break;
}
//
// mark this VIEW_INFO as being cleaned-up, so we don't hit it again if
// we don't delete it from the list this time
//
viewInfo->Flags |= VI_CLEANUP;
//
// safe to release the view lock
//
ReleaseViewLock(SessionInfo, ViewType);
//
// now dereference the VIEW_INFO. This may destroy it, but cannot
// destroy the SESSION_INFO, since we added an extra reference in
// CleanupSessions()
//
DereferenceView(viewInfo);
}
DEBUG_LEAVE(0);
}
LPSESSION_INFO
FindOrCreateSession(
IN LPSTR Host,
IN DWORD Port,
OUT LPDWORD Error
)
/*++
Routine Description:
Locates a SESSION_INFO that contains (Host, Port), or creates a new
SESSION_INFO
BUGBUG - need to do the following:
resolve the host name
find host by name/port or address/port
Arguments:
Host - pointer to host name where gopher server lives
Port - port at which gopher server listens
Error - place to return error
Return Value:
LPSESSION_INFO
Success - pointer to session info. Created contains TRUE if we created
the SESSION_INFO, else FALSE
Failure - NULL
Error contains reason for failure
--*/
{
LPSESSION_INFO sessionInfo;
BOOL found;
AcquireSessionLock();
found = FALSE;
for (sessionInfo = (LPSESSION_INFO)SessionList.List.Flink;
sessionInfo != (LPSESSION_INFO)&SessionList.List;
sessionInfo = (LPSESSION_INFO)(sessionInfo->List.Flink)) {
if (!stricmp(sessionInfo->Host, Host) && (sessionInfo->Port == Port)) {
found = TRUE;
break;
}
}
if (!found) {
sessionInfo = CreateSession(Host, Port, Error);
if (sessionInfo != NULL) {
InsertAtHeadOfSerializedList(&SessionList, &sessionInfo->List);
}
}
if (sessionInfo != NULL) {
//
// the reference count will be at least 2
//
ReferenceSession(sessionInfo);
}
ReleaseSessionLock();
return sessionInfo;
}
PRIVATE
LPSESSION_INFO
CreateSession(
IN LPSTR Host,
IN DWORD Port,
OUT LPDWORD Error
)
/*++
Routine Description:
Creates and initializes a SESSION_INFO 'object'
Arguments:
Host - pointer to host name/ip address
Port - host port
Error - place to return reason for failure
Return Value:
LPSESSION_INFO
Success - pointer to initialized session info
Failure - NULL
Error contains reason for failure
--*/
{
LPSESSION_INFO sessionInfo;
LPSTR hostName = NULL;
DWORD error;
DEBUG_ENTER((DBG_GOPHER,
Pointer,
"CreateSession",
"%q, %d, %x",
Host,
Port,
Error
));
sessionInfo = NEW(SESSION_INFO);
if (sessionInfo != NULL) {
hostName = NEW_STRING(Host);
if (hostName != NULL) {
error = AllocateHandle((LPVOID)sessionInfo, &sessionInfo->Handle);
if (error == ERROR_SUCCESS) {
InitializeListHead(&sessionInfo->List);
sessionInfo->Host = hostName;
sessionInfo->Port = Port;
InitializeSerializedList(&sessionInfo->FindList);
InitializeSerializedList(&sessionInfo->FileList);
SESSION_CREATED();
}
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
if (error != ERROR_SUCCESS) {
if (hostName != NULL) {
DEL_STRING(hostName);
}
if (sessionInfo != NULL) {
DEL(sessionInfo);
}
sessionInfo = NULL;
}
DEBUG_ERROR(SESSION, error);
*Error = error;
DEBUG_LEAVE(sessionInfo);
return sessionInfo;
}
PRIVATE
VOID
DestroySession(
IN LPSESSION_INFO SessionInfo
)
/*++
Routine Description:
Opposite of CreateSession - removes a SESSION_INFO from SessionList and
frees all resources owned by the SESSION_INFO and finally frees the memory
Assumes: 1. SessionListLock is held
2. SessionInfo is not on any lists
3. The SERIALIZED_LISTs have already been created
Arguments:
SessionInfo - pointer to SESSION_INFO to delete
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_GOPHER,
None,
"DestroySession",
"%x",
SessionInfo
));
INET_DEBUG_ASSERT(SessionInfo->List.Flink == NULL);
INET_DEBUG_ASSERT(SessionInfo->List.Blink == NULL);
INET_ASSERT(SessionInfo->ReferenceCount == 0);
INET_ASSERT(IsSerializedListEmpty(&SessionInfo->FindList));
INET_DEBUG_ASSERT(!IsLockHeld(&SessionInfo->FindList));
INET_ASSERT(IsSerializedListEmpty(&SessionInfo->FileList));
INET_DEBUG_ASSERT(!IsLockHeld(&SessionInfo->FileList));
if (SessionInfo->Handle) {
FreeHandle(SessionInfo->Handle);
}
if (SessionInfo->Host != NULL) {
DEL(SessionInfo->Host);
}
TerminateSerializedList(&SessionInfo->FindList);
TerminateSerializedList(&SessionInfo->FileList);
DEL(SessionInfo);
SESSION_DESTROYED();
DEBUG_LEAVE(0);
}
VOID
ReferenceSession(
IN LPSESSION_INFO SessionInfo
)
/*++
Routine Description:
Increases the reference count of a SESSION_INFO
Arguments:
Session - pointer to SESSION_INFO to reference
Return Value:
None.
--*/
{
INET_ASSERT(SessionInfo != NULL);
InterlockedIncrement(&SessionInfo->ReferenceCount);
}
LPSESSION_INFO
DereferenceSession(
IN LPSESSION_INFO SessionInfo
)
/*++
Routine Description:
Reduces the reference count of a SESSION_INFO. If it goes to zero, the
SESSION_INFO is removed from the SessionList and is deallocated
Arguments:
SessionInfo - pointer to SESSION_INFO to dereference
Return Value:
LPSESSION_INFO
NULL - SessionInfo was deleted
!NULL - Reference count still >0
--*/
{
INET_ASSERT(SessionInfo);
INET_ASSERT(SessionInfo->ReferenceCount >= 1);
//
// use InterlockedDecrement to dereference the session. If it goes to zero
// acquire the session lock, check if the reference count is still zero,
// and if so, remove the session from the session list
//
if (InterlockedDecrement(&SessionInfo->ReferenceCount) == 0) {
AcquireSessionLock();
if (SessionInfo->ReferenceCount == 0) {
RemoveFromSerializedList(&SessionList, (PLIST_ENTRY)SessionInfo);
DestroySession(SessionInfo);
SessionInfo = NULL;
}
ReleaseSessionLock();
}
return SessionInfo;
}
VOID
AcquireViewLock(
IN LPSESSION_INFO SessionInfo,
IN VIEW_TYPE ViewType
)
/*++
Routine Description:
Acquires one of the SessionInfo View locks
Arguments:
SessionInfo - pointer to SESSION_INFO
ViewType - identifies which list to lock
Return Value:
None.
--*/
{
LPSERIALIZED_LIST list;
INET_ASSERT(SessionInfo != NULL);
INET_ASSERT((ViewType == ViewTypeFile) || (ViewType == ViewTypeFind));
list = (ViewType == ViewTypeFile)
? &SessionInfo->FileList
: &SessionInfo->FindList
;
LockSerializedList(list);
}
VOID
ReleaseViewLock(
IN LPSESSION_INFO SessionInfo,
IN VIEW_TYPE ViewType
)
/*++
Routine Description:
Releases the SessionInfo View lock
Arguments:
SessionInfo - pointer to SESSION_INFO
ViewType - identifies which list to lock
Return Value:
None.
--*/
{
LPSERIALIZED_LIST list;
INET_ASSERT(SessionInfo != NULL);
INET_ASSERT((ViewType == ViewTypeFile) || (ViewType == ViewTypeFind));
list = (ViewType == ViewTypeFile)
? &SessionInfo->FileList
: &SessionInfo->FindList
;
UnlockSerializedList(list);
}
DWORD
GopherTransaction(
IN LPVIEW_INFO ViewInfo
)
/*++
Routine Description:
Performs an 'atomic' gopher operation. Connects to a server (if it isn't
already connected (in the future?)), sends a request and receives the
entire response message. The connection is terminated
Arguments:
ViewInfo - pointer to VIEW_INFO describing gopher server to talk to,
request and buffer for response
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - Winsock error
--*/
{
DWORD error;
INET_ASSERT(ViewInfo != NULL);
error = GopherConnect(ViewInfo);
if (error == ERROR_SUCCESS) {
error = GopherSendRequest(ViewInfo);
if (error == ERROR_SUCCESS) {
DWORD bytesReceived;
//
// receive the first part of the response. We don't care about the
// number of bytes received at this point
//
// If the response is completed and the connection is not persistent
// or an error occurs, the connection will be closed
//
ViewInfo->BufferInfo->Flags |= BI_FIRST_RECEIVE;
error = GopherReceiveResponse(ViewInfo, &bytesReceived);
}
}
return error;
}
DWORD
IsServerGopherPlus(
IN LPSESSION_INFO SessionInfo,
OUT LPBOOL Answer
)
/*++
Routine Description:
Tries to determine whether a gopher server identified by Session is gopher+.
The caller should already have determined that we don't know the type of
gopher server described by Session and should modify the flags based on a
successful return from this function
Arguments:
SessionInfo - pointer to SESSION_INFO describing the (unknown) gopher server
Answer - pointer to place to put the answer
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
/*
DWORD error;
BYTE buffer[GOPHER_PLUS_INFO_TOKEN_LENGTH + 2]; // "+INFO"
// + 1 for possible ':'
// + 1 for ' '
BOOL receiveComplete;
//
// in order to find out the type of gopher server, we send a request for
// gopher+ info of the root directory. We will get back either the gopher+
// information or a gopher0 server will return the directory list (or we
// will get an error). Therefore, if the transaction doesn't result in an
// error, we can say that if the buffer starts with "+INFO" then the
// server is gopher+ else plain gopher
//
error = GopherTransaction(SessionInfo,
GOPHER_PLUS_INFO_REQUEST,
TRUE,
sizeof(buffer),
buffer,
NULL,
&receiveComplete
);
//
// in both gopher+ and gopher server cases, the server should want to
// return more data than we've supplied buffer for (7 bytes!). In the
// case of gopher+, it will be trying to return the +INFO block for
// the directory entry. In the case of gopher, it will be trying to
// return the entire default directory list. Either way, we don't care
// to take the data: "+INFO[:] " being present or not at the start of
// the buffer is good enough for us, so just close the session and
// examine what we have
//
DisconnectFromServer(SessionInfo, );
if (error == ERROR_SUCCESS) {
register DWORD matchLength;
matchLength = IsGopherPlusToken(GOPHER_PLUS_INFO_TOKEN,
GOPHER_PLUS_INFO_TOKEN_LENGTH,
buffer,
sizeof(buffer)
);
*Answer = (BOOL)(matchLength != 0);
IF_DEBUG(SESSION) {
DBGPRINT(DBG_INFO,
"IsServerGopherPlus",
("Server \"%s\" %s gopher+\n",
SessionInfo->Host,
(matchLength == 0) ? "NOT" : "IS"
));
}
} else {
IF_DEBUG(SESSION) {
DBGPRINT(DBG_ERROR,
"IsServerGopherPlus",
("GopherTransaction() returns %d\n",
error
));
}
}
return error;
*/
*Answer = FALSE;
return ERROR_SUCCESS;
}
BOOL
IsGopherPlusSession(
IN LPSESSION_INFO SessionInfo
)
/*++
Routine Description:
Returns TRUE if Session is a session with a gopher+ server
Arguments:
SessionInfo - pointer to SESSION_INFO describing gopher[+] server
Return Value:
BOOL
--*/
{
return (BOOL)SessionInfo->Flags & SI_GOPHER_PLUS;
}
DWORD
SearchSessionsForAttribute(
IN LPSTR Locator,
IN LPSTR Attribute,
IN LPBYTE Buffer,
IN OUT LPDWORD BufferLength
)
/*++
Routine Description:
Searches all VIEW_INFO buffers for a requested Locator and extracts the
attributes if found
Arguments:
Locator - pointer to locator describing item to get attributes for
Attribute - pointer to string describing attribute(s) to get
Buffer - pointer to buffer in which to return attribute strings
BufferLength - IN: length of Buffer in bytes
OUT: length of returned attribute strings in bytes,
excluding any terminating NUL
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
ERROR_INSUFFICIENT_BUFFER
--*/
{
LPSESSION_INFO session;
BOOL found = FALSE;
DWORD error = ERROR_SUCCESS;
AcquireSessionLock();
/*
for (session = (LPSESSION_INFO)SessionList.Flink;
session != (LPSESSION_INFO)&SessionList.Flink;
session = (LPSESSION_INFO)session->List.Flink) {
LPVIEW_INFO viewInfo;
AcquireFindLock(session);
for (findInfo = (LPVIEW_INFO)session->FindList.Flink;
findInfo != (LPVIEW_INFO)&session->FindList;
findInfo = (LPVIEW_INFO)findInfo->List.Flink) {
ReferenceFind(findInfo);
if (findInfo->Handle) {
found = TRUE;
break; // out of for()
}
}
if (found) {
break; // out of while()
}
ReleaseFindLock(session);
}
if (found) {
error = ERROR_SUCCESS;
} else {
error = ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
}
*/
ReleaseSessionLock();
error = ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
return error;
}