Commit 63e6a4b2 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

wininet: Moved WORKREQUEST allocation to INTERNET_AsyncCall callers.

parent c9ca9c5a
......@@ -316,17 +316,15 @@ BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
WORKREQUEST *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
struct WORKREQ_FTPPUTFILEW *req = &task->u.FtpPutFileW;
workRequest.asyncproc = AsyncFtpPutFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req->lpszLocalFile = heap_strdupW(lpszLocalFile);
req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
req->dwFlags = dwFlags;
req->dwContext = dwContext;
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -488,15 +486,14 @@ BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpSetCurrentDirectoryW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
req = &task->u.FtpSetCurrentDirectoryW;
req->lpszDirectory = heap_strdupW(lpszDirectory);
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -638,15 +635,14 @@ BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPCREATEDIRECTORYW *req;
workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpCreateDirectoryW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
req = &task->u.FtpCreateDirectoryW;
req->lpszDirectory = heap_strdupW(lpszDirectory);
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -782,18 +778,17 @@ HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPFINDFIRSTFILEW *req;
workRequest.asyncproc = AsyncFtpFindFirstFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpFindFirstFileW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
req = &task->u.FtpFindFirstFileW;
req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
req->lpFindFileData = lpFindFileData;
req->dwFlags = dwFlags;
req->dwContext= dwContext;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
r = NULL;
}
else
......@@ -998,16 +993,15 @@ BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDir
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpGetCurrentDirectoryW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
req = &task->u.FtpGetCurrentDirectoryW;
req->lpszDirectory = lpszCurrentDirectory;
req->lpdwDirectory = lpdwCurrentDirectory;
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -1271,14 +1265,12 @@ static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available,
retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
if(retval > 0) {
WORKREQUEST workRequest;
task_header_t *task;
*available = 0;
workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
workRequest.hdr = WININET_AddRef( &file->hdr );
INTERNET_AsyncCall(&workRequest);
task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
INTERNET_AsyncCall(task);
*available = 0;
return ERROR_IO_PENDING;
}
}
......@@ -1512,18 +1504,17 @@ HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPOPENFILEW *req;
workRequest.asyncproc = AsyncFtpOpenFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpOpenFileW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
req = &task->u.FtpOpenFileW;
req->lpszFilename = heap_strdupW(lpszFileName);
req->dwAccess = fdwAccess;
req->dwFlags = dwFlags;
req->dwContext = dwContext;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
r = NULL;
}
else
......@@ -1633,12 +1624,11 @@ BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lps
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPGETFILEW *req;
workRequest.asyncproc = AsyncFtpGetFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpGetFileW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
req = &task->u.FtpGetFileW;
req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
req->lpszNewFile = heap_strdupW(lpszNewFile);
req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
......@@ -1646,7 +1636,7 @@ BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lps
req->dwFlags = dwInternetFlags;
req->dwContext = dwContext;
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -1827,15 +1817,14 @@ BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPDELETEFILEW *req;
workRequest.asyncproc = AsyncFtpDeleteFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpDeleteFileW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
req = &task->u.FtpDeleteFileW;
req->lpszFilename = heap_strdupW(lpszFileName);
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -1972,15 +1961,14 @@ BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPREMOVEDIRECTORYW *req;
workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpRemoveDirectoryW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
req = &task->u.FtpRemoveDirectoryW;
req->lpszDirectory = heap_strdupW(lpszDirectory);
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -2122,16 +2110,15 @@ BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszD
hIC = lpwfs->lpAppInfo;
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPRENAMEFILEW *req;
workRequest.asyncproc = AsyncFtpRenameFileProc;
workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
req = &workRequest.u.FtpRenameFileW;
task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
req = &task->u.FtpRenameFileW;
req->lpszSrcFile = heap_strdupW(lpszSrc);
req->lpszDestFile = heap_strdupW(lpszDest);
r = res_to_le(INTERNET_AsyncCall(&workRequest));
r = res_to_le(INTERNET_AsyncCall(task));
}
else
{
......@@ -3447,15 +3434,14 @@ static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_FTPFINDNEXTW *req;
workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
workRequest.hdr = WININET_AddRef( &find->hdr );
req = &workRequest.u.FtpFindNextW;
task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
req = &task->u.FtpFindNextW;
req->lpFindFileData = data;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
return ERROR_SUCCESS;
}
......
......@@ -2877,7 +2877,7 @@ static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DW
return res;
}
static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
static void AsyncReadFileExProc(WORKREQUEST *workRequest)
{
struct WORKREQ_HTTPREADFILEEX const *data = &workRequest->u.HttpReadFileEx;
http_request_t *req = (http_request_t*)workRequest->hdr;
......@@ -2904,7 +2904,7 @@ static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWO
if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
if (TryEnterCriticalSection( &req->read_section ))
{
......@@ -2917,13 +2917,12 @@ static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWO
LeaveCriticalSection( &req->read_section );
}
workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
workRequest.hdr = WININET_AddRef(&req->hdr);
workRequest.u.HttpReadFileEx.buf = buf;
workRequest.u.HttpReadFileEx.size = size;
workRequest.u.HttpReadFileEx.ret_read = ret_read;
task = alloc_async_task(&req->hdr, AsyncReadFileExProc, sizeof(*task));
task->u.HttpReadFileEx.buf = buf;
task->u.HttpReadFileEx.size = size;
task->u.HttpReadFileEx.ret_read = ret_read;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
return ERROR_IO_PENDING;
}
......@@ -2988,7 +2987,7 @@ static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD s
return res;
}
static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
static void AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
{
http_request_t *req = (http_request_t*)workRequest->hdr;
......@@ -3003,7 +3002,7 @@ static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available,
if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
task_header_t *task;
/* never wait, if we can't enter the section we queue an async request right away */
if (TryEnterCriticalSection( &req->read_section ))
......@@ -3014,11 +3013,8 @@ static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available,
LeaveCriticalSection( &req->read_section );
}
workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
workRequest.hdr = WININET_AddRef( &req->hdr );
INTERNET_AsyncCall(&workRequest);
task = alloc_async_task(&req->hdr, AsyncQueryDataAvailableProc, sizeof(*task));
INTERNET_AsyncCall(task);
return ERROR_IO_PENDING;
}
......@@ -5101,17 +5097,15 @@ BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST work;
WORKREQUEST *task;
struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
work.asyncproc = AsyncHttpEndRequestProc;
work.hdr = WININET_AddRef( &request->hdr );
work_endrequest = &work.u.HttpEndRequestW;
task = alloc_async_task(&request->hdr, AsyncHttpEndRequestProc, sizeof(*task));
work_endrequest = &task->u.HttpEndRequestW;
work_endrequest->dwFlags = dwFlags;
work_endrequest->dwContext = dwContext;
INTERNET_AsyncCall(&work);
INTERNET_AsyncCall(task);
res = ERROR_IO_PENDING;
}
else
......@@ -5217,12 +5211,11 @@ BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_HTTPSENDREQUESTW *req;
workRequest.asyncproc = AsyncHttpSendRequestProc;
workRequest.hdr = WININET_AddRef( &request->hdr );
req = &workRequest.u.HttpSendRequestW;
task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
req = &task->u.HttpSendRequestW;
if (lpBuffersIn)
{
DWORD size = 0;
......@@ -5255,7 +5248,7 @@ BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
req->bEndRequest = FALSE;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
/*
* This is from windows.
*/
......@@ -5324,12 +5317,11 @@ BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_HTTPSENDREQUESTW *req;
workRequest.asyncproc = AsyncHttpSendRequestProc;
workRequest.hdr = WININET_AddRef( &request->hdr );
req = &workRequest.u.HttpSendRequestW;
task = alloc_async_task(&request->hdr, AsyncHttpSendRequestProc, sizeof(*task));
req = &task->u.HttpSendRequestW;
if (lpszHeaders)
{
DWORD size;
......@@ -5348,10 +5340,7 @@ BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
req->dwContentLength = dwOptionalLength;
req->bEndRequest = TRUE;
INTERNET_AsyncCall(&workRequest);
/*
* This is from windows.
*/
INTERNET_AsyncCall(task);
res = ERROR_IO_PENDING;
}
else
......
......@@ -3536,22 +3536,18 @@ HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
}
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) {
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_INTERNETOPENURLW *req;
workRequest.asyncproc = AsyncInternetOpenUrlProc;
workRequest.hdr = WININET_AddRef( &hIC->hdr );
req = &workRequest.u.InternetOpenUrlW;
task = alloc_async_task(&hIC->hdr, AsyncInternetOpenUrlProc, sizeof(*task));
req = &task->u.InternetOpenUrlW;
req->lpszUrl = heap_strdupW(lpszUrl);
req->lpszHeaders = heap_strdupW(lpszHeaders);
req->dwHeadersLength = dwHeadersLength;
req->dwFlags = dwFlags;
req->dwContext = dwContext;
INTERNET_AsyncCall(&workRequest);
/*
* This is from windows.
*/
INTERNET_AsyncCall(task);
SetLastError(ERROR_IO_PENDING);
} else {
ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
......@@ -3676,16 +3672,13 @@ DWORD INTERNET_GetLastError(void)
*/
static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
{
LPWORKREQUEST lpRequest = lpvParam;
WORKREQUEST workRequest;
task_header_t *task = lpvParam;
TRACE("\n");
workRequest = *lpRequest;
heap_free(lpRequest);
workRequest.asyncproc(&workRequest);
WININET_Release( workRequest.hdr );
task->proc(task);
WININET_Release(task->hdr);
heap_free(task);
if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
{
......@@ -3695,6 +3688,18 @@ static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
return TRUE;
}
void *alloc_async_task(object_header_t *hdr, async_task_proc_t proc, size_t size)
{
task_header_t *task;
task = heap_alloc(size);
if(!task)
return NULL;
task->hdr = WININET_AddRef(hdr);
task->proc = proc;
return task;
}
/***********************************************************************
* INTERNET_AsyncCall (internal)
......@@ -3707,20 +3712,13 @@ static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
DWORD INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
{
BOOL bSuccess;
LPWORKREQUEST lpNewRequest;
TRACE("\n");
lpNewRequest = heap_alloc(sizeof(WORKREQUEST));
if (!lpNewRequest)
return ERROR_OUTOFMEMORY;
*lpNewRequest = *lpWorkRequest;
bSuccess = QueueUserWorkItem(INTERNET_WorkerThreadFunc, lpNewRequest, WT_EXECUTELONGFUNCTION);
bSuccess = QueueUserWorkItem(INTERNET_WorkerThreadFunc, lpWorkRequest, WT_EXECUTELONGFUNCTION);
if (!bSuccess)
{
heap_free(lpNewRequest);
heap_free(lpWorkRequest);
return ERROR_INTERNET_ASYNC_THREAD_FAILED;
}
return ERROR_SUCCESS;
......
......@@ -468,9 +468,12 @@ struct WORKREQ_HTTPREADFILEEX
DWORD *ret_read;
};
typedef struct WORKREQ task_header_t;
typedef void (*async_task_proc_t)(task_header_t*);
typedef struct WORKREQ
{
void (*asyncproc)(struct WORKREQ*);
async_task_proc_t proc;
object_header_t *hdr;
union {
......@@ -491,9 +494,10 @@ typedef struct WORKREQ
struct WORKREQ_INTERNETOPENURLW InternetOpenUrlW;
struct WORKREQ_HTTPREADFILEEX HttpReadFileEx;
} u;
} WORKREQUEST, *LPWORKREQUEST;
void *alloc_async_task(object_header_t*,async_task_proc_t,size_t) DECLSPEC_HIDDEN;
void *alloc_object(object_header_t*,const object_vtbl_t*,size_t) DECLSPEC_HIDDEN;
object_header_t *get_handle_object( HINTERNET hinternet ) DECLSPEC_HIDDEN;
object_header_t *WININET_AddRef( object_header_t *info ) DECLSPEC_HIDDEN;
......
......@@ -385,7 +385,7 @@ void SendAsyncCallback(object_header_t *hdr, DWORD_PTR dwContext,
if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
{
WORKREQUEST workRequest;
WORKREQUEST *task;
struct WORKREQ_SENDCALLBACK *req;
void *lpvStatusInfo_copy = lpvStatusInfo;
......@@ -395,15 +395,14 @@ void SendAsyncCallback(object_header_t *hdr, DWORD_PTR dwContext,
memcpy(lpvStatusInfo_copy, lpvStatusInfo, dwStatusInfoLength);
}
workRequest.asyncproc = SendAsyncCallbackProc;
workRequest.hdr = WININET_AddRef( hdr );
req = &workRequest.u.SendCallback;
task = alloc_async_task(hdr, SendAsyncCallbackProc, sizeof(*task));
req = &task->u.SendCallback;
req->dwContext = dwContext;
req->dwInternetStatus = dwInternetStatus;
req->lpvStatusInfo = lpvStatusInfo_copy;
req->dwStatusInfoLength = dwStatusInfoLength;
INTERNET_AsyncCall(&workRequest);
INTERNET_AsyncCall(task);
}
else
INTERNET_SendCallback(hdr, dwContext, dwInternetStatus,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment