2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <winpr/crt.h>
|
|
|
|
#include <winpr/pipe.h>
|
|
|
|
#include <winpr/file.h>
|
|
|
|
#include <winpr/tchar.h>
|
|
|
|
#include <winpr/winpr.h>
|
2014-08-18 19:22:22 +04:00
|
|
|
#include <winpr/wlog.h>
|
2013-07-23 23:03:08 +04:00
|
|
|
#include <winpr/print.h>
|
|
|
|
#include <winpr/synch.h>
|
|
|
|
#include <winpr/thread.h>
|
|
|
|
|
|
|
|
#define PIPE_BUFFER_SIZE 32
|
2016-06-01 17:26:26 +03:00
|
|
|
#define PIPE_TIMEOUT_MS 20000 // 20 seconds
|
2013-07-23 23:03:08 +04:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
BYTE SERVER_MESSAGE[PIPE_BUFFER_SIZE];
|
|
|
|
BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE];
|
|
|
|
|
|
|
|
BOOL bClientSuccess = FALSE;
|
|
|
|
BOOL bServerSuccess = FALSE;
|
|
|
|
|
|
|
|
static HANDLE serverReadyEvent;
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped");
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
static void* named_pipe_client_thread(void* arg)
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
|
|
|
DWORD status;
|
2015-06-23 22:29:21 +03:00
|
|
|
HANDLE hEvent = NULL;
|
|
|
|
HANDLE hNamedPipe = NULL;
|
|
|
|
BYTE* lpReadBuffer = NULL;
|
2013-07-23 23:03:08 +04:00
|
|
|
BOOL fSuccess = FALSE;
|
|
|
|
OVERLAPPED overlapped;
|
|
|
|
DWORD nNumberOfBytesToRead;
|
|
|
|
DWORD nNumberOfBytesToWrite;
|
|
|
|
DWORD NumberOfBytesTransferred;
|
2015-06-23 13:25:07 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS);
|
|
|
|
if (status != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
printf("client: failed to wait for server ready event: %u\n", status);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
/* 1: initialize overlapped structure */
|
|
|
|
|
|
|
|
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
|
|
|
|
if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: CreateEvent failure: %u\n", GetLastError());
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
2016-06-01 17:26:26 +03:00
|
|
|
overlapped.hEvent = hEvent;
|
|
|
|
|
|
|
|
|
|
|
|
/* 2: connect to server named pipe */
|
|
|
|
|
|
|
|
hNamedPipe = CreateFile(lpszPipeName, GENERIC_READ | GENERIC_WRITE,
|
|
|
|
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: Named Pipe CreateFile failure: %u\n", GetLastError());
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
|
|
|
|
/* 3: write to named pipe */
|
|
|
|
|
|
|
|
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
|
|
|
NumberOfBytesTransferred = 0;
|
|
|
|
|
|
|
|
fSuccess = WriteFile(hNamedPipe, CLIENT_MESSAGE, nNumberOfBytesToWrite, NULL, &overlapped);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
2015-04-03 17:21:01 +03:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: NamedPipe WriteFile failure (initial): %u\n", GetLastError());
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2015-04-03 17:21:01 +03:00
|
|
|
}
|
2015-04-28 18:00:41 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
|
|
|
if (status != WAIT_OBJECT_0)
|
2015-04-28 18:00:41 +03:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: failed to wait for overlapped event (write): %u\n", status);
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2015-04-28 18:00:41 +03:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
2013-07-23 23:03:08 +04:00
|
|
|
if (!fSuccess)
|
2016-06-01 17:26:26 +03:00
|
|
|
{
|
|
|
|
printf("client: NamedPipe WriteFile failure (final): %u\n", GetLastError());
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
printf("client: WriteFile transferred %u bytes:\n", NumberOfBytesTransferred);
|
2013-07-23 23:03:08 +04:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
|
|
|
|
/* 4: read from named pipe */
|
|
|
|
|
|
|
|
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: Error allocating read buffer\n");
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
2016-06-01 17:26:26 +03:00
|
|
|
NumberOfBytesTransferred = 0;
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, NULL, &overlapped);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("client: NamedPipe ReadFile failure (initial): %u\n", GetLastError());
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = WaitForMultipleObjects(1, &hEvent, FALSE, PIPE_TIMEOUT_MS);
|
|
|
|
if (status != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
printf("client: failed to wait for overlapped event (read): %u\n", status);
|
2015-06-23 13:25:07 +03:00
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, TRUE);
|
2016-06-01 17:26:26 +03:00
|
|
|
if (!fSuccess)
|
|
|
|
{
|
|
|
|
printf("client: NamedPipe ReadFile failure (final): %u\n", GetLastError());
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("client: ReadFile transferred %u bytes:\n", NumberOfBytesTransferred);
|
2014-08-18 19:22:22 +04:00
|
|
|
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
2015-06-23 13:25:07 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE || memcmp(lpReadBuffer, SERVER_MESSAGE, PIPE_BUFFER_SIZE))
|
|
|
|
{
|
|
|
|
printf("client: received unexpected data from server\n");
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("client: finished successfully\n");
|
|
|
|
bClientSuccess = TRUE;
|
|
|
|
|
2015-06-23 13:25:07 +03:00
|
|
|
finish:
|
2013-07-23 23:03:08 +04:00
|
|
|
free(lpReadBuffer);
|
2015-06-23 13:25:07 +03:00
|
|
|
if (hNamedPipe)
|
|
|
|
CloseHandle(hNamedPipe);
|
|
|
|
if (hEvent)
|
|
|
|
CloseHandle(hEvent);
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
static void* named_pipe_server_thread(void* arg)
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
|
|
|
DWORD status;
|
2016-06-01 17:26:26 +03:00
|
|
|
HANDLE hEvent = NULL;
|
|
|
|
HANDLE hNamedPipe = NULL;
|
|
|
|
BYTE* lpReadBuffer = NULL;
|
2013-07-23 23:03:08 +04:00
|
|
|
OVERLAPPED overlapped;
|
|
|
|
BOOL fSuccess = FALSE;
|
|
|
|
BOOL fConnected = FALSE;
|
|
|
|
DWORD nNumberOfBytesToRead;
|
|
|
|
DWORD nNumberOfBytesToWrite;
|
|
|
|
DWORD NumberOfBytesTransferred;
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
/* 1: initialize overlapped structure */
|
|
|
|
|
|
|
|
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
|
|
|
|
if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: CreateEvent failure: %u\n", GetLastError());
|
|
|
|
SetEvent(serverReadyEvent); /* unblock client thread */
|
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
2016-06-01 17:26:26 +03:00
|
|
|
overlapped.hEvent = hEvent;
|
|
|
|
|
|
|
|
|
|
|
|
/* 2: create named pipe and set ready event */
|
|
|
|
|
|
|
|
hNamedPipe = CreateNamedPipe(lpszPipeName,
|
|
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
|
|
|
PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, NULL);
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: CreateNamedPipe failure: %u\n", GetLastError());
|
|
|
|
SetEvent(serverReadyEvent); /* unblock client thread */
|
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
SetEvent(serverReadyEvent);
|
2015-04-28 18:00:41 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
/* 3: connect named pipe */
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* This sleep will most certainly cause ERROR_PIPE_CONNECTED below */
|
|
|
|
Sleep(2000);
|
|
|
|
#endif
|
2015-04-28 18:00:41 +03:00
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
fConnected = ConnectNamedPipe(hNamedPipe, &overlapped);
|
2016-06-01 17:26:26 +03:00
|
|
|
status = GetLastError();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* At this point if fConnected is FALSE, we have to check GetLastError() for:
|
|
|
|
* ERROR_PIPE_CONNECTED:
|
|
|
|
* client has already connected before we have called ConnectNamedPipe.
|
|
|
|
* this is quite common depending on the timings and indicates success
|
|
|
|
* ERROR_IO_PENDING:
|
|
|
|
* Since we're using ConnectNamedPipe asynchronously here, the function returns
|
|
|
|
* immediately and this error code simply indicates that the operation is
|
|
|
|
* still in progress. Hence we have to wait for the completion event and use
|
|
|
|
* GetOverlappedResult to query the actual result of the operation (note that
|
|
|
|
* the lpNumberOfBytesTransferred parameter is undefined/useless for a
|
|
|
|
* ConnectNamedPipe operation)
|
|
|
|
*/
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
if (!fConnected)
|
2016-06-01 17:26:26 +03:00
|
|
|
fConnected = (status == ERROR_PIPE_CONNECTED);
|
2013-07-23 23:03:08 +04:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: ConnectNamedPipe status: %u\n", status);
|
|
|
|
|
|
|
|
if (!fConnected && status == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
DWORD dwDummy;
|
|
|
|
printf("server: waiting up to %u ms for connection ...\n", PIPE_TIMEOUT_MS);
|
|
|
|
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
|
|
fConnected = GetOverlappedResult(hNamedPipe, &overlapped, &dwDummy, FALSE);
|
|
|
|
else
|
|
|
|
printf("server: failed to wait for overlapped event (connect): %u\n", status);
|
|
|
|
}
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
if (!fConnected)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: ConnectNamedPipe failed: %u\n", status);
|
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: named pipe successfully connected\n");
|
|
|
|
|
|
|
|
|
|
|
|
/* 4: read from named pipe */
|
|
|
|
|
|
|
|
if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE)))
|
2015-04-03 17:21:01 +03:00
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: Error allocating read buffer\n");
|
|
|
|
goto finish;
|
2015-04-03 17:21:01 +03:00
|
|
|
}
|
2016-06-01 17:26:26 +03:00
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
nNumberOfBytesToRead = PIPE_BUFFER_SIZE;
|
2016-06-01 17:26:26 +03:00
|
|
|
NumberOfBytesTransferred = 0;
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, NULL, &overlapped);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: NamedPipe ReadFile failure (initial): %u\n", GetLastError());
|
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
|
|
|
if (status != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
printf("server: failed to wait for overlapped event (read): %u\n", status);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
|
|
|
if (!fSuccess)
|
|
|
|
{
|
|
|
|
printf("server: NamedPipe ReadFile failure (final): %u\n", GetLastError());
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("server: ReadFile transferred %u bytes:\n", NumberOfBytesTransferred);
|
2014-08-18 19:22:22 +04:00
|
|
|
winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred);
|
2016-06-01 17:26:26 +03:00
|
|
|
|
|
|
|
if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE || memcmp(lpReadBuffer, CLIENT_MESSAGE, PIPE_BUFFER_SIZE))
|
|
|
|
{
|
|
|
|
printf("server: received unexpected data from client\n");
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 5: write to named pipe */
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
nNumberOfBytesToWrite = PIPE_BUFFER_SIZE;
|
2016-06-01 17:26:26 +03:00
|
|
|
NumberOfBytesTransferred = 0;
|
|
|
|
|
|
|
|
fSuccess = WriteFile(hNamedPipe, SERVER_MESSAGE, nNumberOfBytesToWrite, NULL, &overlapped);
|
2013-07-23 23:03:08 +04:00
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
fSuccess = (GetLastError() == ERROR_IO_PENDING);
|
|
|
|
|
|
|
|
if (!fSuccess)
|
|
|
|
{
|
2016-06-01 17:26:26 +03:00
|
|
|
printf("server: NamedPipe WriteFile failure (initial): %u\n", GetLastError());
|
|
|
|
goto finish;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS);
|
|
|
|
if (status != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
printf("server: failed to wait for overlapped event (write): %u\n", status);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE);
|
|
|
|
if (!fSuccess)
|
|
|
|
{
|
|
|
|
printf("server: NamedPipe WriteFile failure (final): %u\n", GetLastError());
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("server: WriteFile transferred %u bytes:\n", NumberOfBytesTransferred);
|
|
|
|
//winpr_HexDump("pipe.test", WLOG_DEBUG, lpWriteBuffer, NumberOfBytesTransferred);
|
|
|
|
|
|
|
|
bServerSuccess = TRUE;
|
|
|
|
printf("server: finished successfully\n");
|
|
|
|
|
|
|
|
finish:
|
2013-07-23 23:03:08 +04:00
|
|
|
CloseHandle(hNamedPipe);
|
|
|
|
CloseHandle(hEvent);
|
2016-06-01 17:26:26 +03:00
|
|
|
free(lpReadBuffer);
|
2013-07-23 23:03:08 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[])
|
2013-07-23 23:03:08 +04:00
|
|
|
{
|
|
|
|
HANDLE ClientThread;
|
|
|
|
HANDLE ServerThread;
|
2016-06-01 17:26:26 +03:00
|
|
|
int eFailure = -1;
|
|
|
|
int eSuccess = 0;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
printf("Note: %s result may currently only be trusted on Win32\n", __FUNCTION__);
|
|
|
|
eFailure = eSuccess;
|
|
|
|
#endif
|
2015-04-28 18:00:41 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA);
|
|
|
|
FillMemory(CLIENT_MESSAGE, PIPE_BUFFER_SIZE, 0xBB);
|
|
|
|
|
|
|
|
if (!(serverReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
2015-04-28 18:00:41 +03:00
|
|
|
{
|
|
|
|
printf("CreateEvent failed: %d\n", GetLastError());
|
2016-06-01 17:26:26 +03:00
|
|
|
return eFailure;
|
2015-04-28 18:00:41 +03:00
|
|
|
}
|
|
|
|
if (!(ClientThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) named_pipe_client_thread, NULL, 0, NULL)))
|
|
|
|
{
|
|
|
|
printf("CreateThread (client) failed: %d\n", GetLastError());
|
2016-06-01 17:26:26 +03:00
|
|
|
return eFailure;
|
2015-04-28 18:00:41 +03:00
|
|
|
}
|
|
|
|
if (!(ServerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) named_pipe_server_thread, NULL, 0, NULL)))
|
|
|
|
{
|
|
|
|
printf("CreateThread (server) failed: %d\n", GetLastError());
|
2016-06-01 17:26:26 +03:00
|
|
|
return eFailure;
|
2015-04-28 18:00:41 +03:00
|
|
|
}
|
|
|
|
|
2013-07-23 23:03:08 +04:00
|
|
|
WaitForSingleObject(ClientThread, INFINITE);
|
|
|
|
WaitForSingleObject(ServerThread, INFINITE);
|
2015-04-28 18:00:41 +03:00
|
|
|
|
2016-06-01 17:26:26 +03:00
|
|
|
if (bClientSuccess && bServerSuccess)
|
|
|
|
return eSuccess;
|
|
|
|
|
|
|
|
return eFailure;
|
2013-07-23 23:03:08 +04:00
|
|
|
}
|