Commit 27202319 authored by Francois Gouget's avatar Francois Gouget Committed by Alexandre Julliard

Make winsock.h and winsock2.h independent from the Unix headers.

Make them compatible with both the Unix C headers and the MSVCRT headers. Ensure compatibility with the Unix headers via the USE_WS_PREFIX macro. Add WINE_NOWINSOCK: prevents winsock.h from being included from windows.h when defined. Add ws2tcpip.h, move definitions to the right header.
parent c3daf422
EXTRADEFS = -DUSE_WS_PREFIX
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
......
......@@ -167,10 +167,10 @@ static int WS_copy_he(char *p_to,char *p_base,int t_size,struct hostent* p_he, i
{
char* p_name,*p_aliases,*p_addr,*p;
struct ws_hostent16 *p_to16 = (struct ws_hostent16*)p_to;
struct ws_hostent32 *p_to32 = (struct ws_hostent32*)p_to;
struct WS_hostent *p_to32 = (struct WS_hostent*)p_to;
int size = hostent_size(p_he) +
(
(flag & AQ_WIN16) ? sizeof(struct ws_hostent16) : sizeof(struct ws_hostent32)
(flag & AQ_WIN16) ? sizeof(struct ws_hostent16) : sizeof(struct WS_hostent)
- sizeof(struct hostent)
);
......@@ -178,7 +178,7 @@ static int WS_copy_he(char *p_to,char *p_base,int t_size,struct hostent* p_he, i
return -size;
p = p_to;
p += (flag & AQ_WIN16) ?
sizeof(struct ws_hostent16) : sizeof(struct ws_hostent32);
sizeof(struct ws_hostent16) : sizeof(struct WS_hostent);
p_name = p;
strcpy(p, p_he->h_name); p += strlen(p) + 1;
p_aliases = p;
......@@ -225,10 +225,10 @@ static int WS_copy_pe(char *p_to,char *p_base,int t_size,struct protoent* p_pe,
{
char* p_name,*p_aliases,*p;
struct ws_protoent16 *p_to16 = (struct ws_protoent16*)p_to;
struct ws_protoent32 *p_to32 = (struct ws_protoent32*)p_to;
struct WS_protoent *p_to32 = (struct WS_protoent*)p_to;
int size = protoent_size(p_pe) +
(
(flag & AQ_WIN16) ? sizeof(struct ws_protoent16) : sizeof(struct ws_protoent32)
(flag & AQ_WIN16) ? sizeof(struct ws_protoent16) : sizeof(struct WS_protoent)
- sizeof(struct protoent)
);
......@@ -236,7 +236,7 @@ static int WS_copy_pe(char *p_to,char *p_base,int t_size,struct protoent* p_pe,
return -size;
p = p_to;
p += (flag & AQ_WIN16) ?
sizeof(struct ws_protoent16) : sizeof(struct ws_protoent32);
sizeof(struct ws_protoent16) : sizeof(struct WS_protoent);
p_name = p;
strcpy(p, p_pe->p_name); p += strlen(p) + 1;
p_aliases = p;
......@@ -279,10 +279,10 @@ static int WS_copy_se(char *p_to,char *p_base,int t_size,struct servent* p_se, i
{
char* p_name,*p_aliases,*p_proto,*p;
struct ws_servent16 *p_to16 = (struct ws_servent16*)p_to;
struct ws_servent32 *p_to32 = (struct ws_servent32*)p_to;
struct WS_servent *p_to32 = (struct WS_servent*)p_to;
int size = servent_size(p_se) +
(
(flag & AQ_WIN16) ? sizeof(struct ws_servent16) : sizeof(struct ws_servent32)
(flag & AQ_WIN16) ? sizeof(struct ws_servent16) : sizeof(struct WS_servent)
- sizeof(struct servent)
);
......@@ -290,7 +290,7 @@ static int WS_copy_se(char *p_to,char *p_base,int t_size,struct servent* p_se, i
return -size;
p = p_to;
p += (flag & AQ_WIN16) ?
sizeof(struct ws_servent16) : sizeof(struct ws_servent32);
sizeof(struct ws_servent16) : sizeof(struct WS_servent);
p_name = p;
strcpy(p, p_se->s_name); p += strlen(p) + 1;
p_proto = p;
......
......@@ -14,14 +14,14 @@ owner ws2_32
5 pascal16 getpeername(word ptr ptr) WINSOCK_getpeername16
6 pascal16 getsockname(word ptr ptr) WINSOCK_getsockname16
7 pascal16 getsockopt(word word word ptr ptr) WINSOCK_getsockopt16
8 pascal htonl(long) WINSOCK_htonl
9 pascal16 htons(word) WINSOCK_htons
10 pascal inet_addr(ptr) WINSOCK_inet_addr
8 pascal htonl(long) WS_htonl
9 pascal16 htons(word) WS_htons
10 pascal inet_addr(ptr) WS_inet_addr
11 pascal inet_ntoa(long) WINSOCK_inet_ntoa16
12 pascal16 ioctlsocket(word long ptr) WINSOCK_ioctlsocket16
13 pascal16 listen(word word) WINSOCK_listen16
14 pascal ntohl(long) WINSOCK_ntohl
15 pascal16 ntohs(word) WINSOCK_ntohs
14 pascal ntohl(long) WS_ntohl
15 pascal16 ntohs(word) WS_ntohs
16 pascal16 recv(word ptr word word) WINSOCK_recv16
17 pascal16 recvfrom(word ptr word word ptr ptr) WINSOCK_recvfrom16
18 pascal16 select(word ptr ptr ptr ptr) WINSOCK_select16
......
......@@ -4,7 +4,7 @@
name ws2_32
type win32
init WSOCK32_LibMain
init WS_LibMain
import user32.dll
import kernel32.dll
......@@ -13,29 +13,29 @@ import ntdll.dll
debug_channels (winsock)
# EXPORTS ***********
1 stdcall accept(long ptr ptr) WSOCK32_accept
2 stdcall bind(long ptr long) WSOCK32_bind
3 stdcall closesocket(long) WSOCK32_closesocket
4 stdcall connect(long ptr long) WSOCK32_connect
5 stdcall getpeername(long ptr ptr) WSOCK32_getpeername
6 stdcall getsockname(long ptr ptr) WSOCK32_getsockname
7 stdcall getsockopt(long long long ptr ptr) WSOCK32_getsockopt
8 stdcall htonl(long) WINSOCK_htonl
9 stdcall htons(long) WINSOCK_htons
10 stdcall ioctlsocket(long long ptr) WSOCK32_ioctlsocket
11 stdcall inet_addr(str) WINSOCK_inet_addr
12 stdcall inet_ntoa(ptr) WSOCK32_inet_ntoa
13 stdcall listen(long long) WSOCK32_listen
14 stdcall ntohl(long) WINSOCK_ntohl
15 stdcall ntohs(long) WINSOCK_ntohs
16 stdcall recv(long ptr long long) WSOCK32_recv
17 stdcall recvfrom(long ptr long long ptr ptr) WSOCK32_recvfrom
18 stdcall select(long ptr ptr ptr ptr) WSOCK32_select
19 stdcall send(long ptr long long) WSOCK32_send
20 stdcall sendto(long ptr long long ptr long) WSOCK32_sendto
21 stdcall setsockopt(long long long ptr long) WSOCK32_setsockopt
22 stdcall shutdown(long long) WSOCK32_shutdown
23 stdcall socket(long long long) WSOCK32_socket
1 stdcall accept(long ptr ptr) WS_accept
2 stdcall bind(long ptr long) WS_bind
3 stdcall closesocket(long) WS_closesocket
4 stdcall connect(long ptr long) WS_connect
5 stdcall getpeername(long ptr ptr) WS_getpeername
6 stdcall getsockname(long ptr ptr) WS_getsockname
7 stdcall getsockopt(long long long ptr ptr) WS_getsockopt
8 stdcall htonl(long) WS_htonl
9 stdcall htons(long) WS_htons
10 stdcall ioctlsocket(long long ptr) WS_ioctlsocket
11 stdcall inet_addr(str) WS_inet_addr
12 stdcall inet_ntoa(ptr) WS_inet_ntoa
13 stdcall listen(long long) WS_listen
14 stdcall ntohl(long) WS_ntohl
15 stdcall ntohs(long) WS_ntohs
16 stdcall recv(long ptr long long) WS_recv
17 stdcall recvfrom(long ptr long long ptr ptr) WS_recvfrom
18 stdcall select(long ptr ptr ptr ptr) WS_select
19 stdcall send(long ptr long long) WS_send
20 stdcall sendto(long ptr long long ptr long) WS_sendto
21 stdcall setsockopt(long long long ptr long) WS_setsockopt
22 stdcall shutdown(long long) WS_shutdown
23 stdcall socket(long long long) WS_socket
24 stdcall WSApSetPostRoutine(ptr) WSApSetPostRoutine
25 stub WPUCompleteOverlappedRequest
26 stub WSAAccept
......@@ -63,13 +63,13 @@ debug_channels (winsock)
48 stub WSAInstallServiceClassA
49 stub WSAInstallServiceClassW
50 stdcall WSAIoctl(long long ptr long ptr long ptr ptr ptr) WSAIoctl
51 stdcall gethostbyaddr(ptr long long) WSOCK32_gethostbyaddr
52 stdcall gethostbyname(str) WSOCK32_gethostbyname
53 stdcall getprotobyname(str) WSOCK32_getprotobyname
54 stdcall getprotobynumber(long) WSOCK32_getprotobynumber
55 stdcall getservbyname(str str) WSOCK32_getservbyname
56 stdcall getservbyport(long str) WSOCK32_getservbyport
57 stdcall gethostname(ptr long) WSOCK32_gethostname
51 stdcall gethostbyaddr(ptr long long) WS_gethostbyaddr
52 stdcall gethostbyname(str) WS_gethostbyname
53 stdcall getprotobyname(str) WS_getprotobyname
54 stdcall getprotobynumber(long) WS_getprotobynumber
55 stdcall getservbyname(str str) WS_getservbyname
56 stdcall getservbyport(long str) WS_getservbyport
57 stdcall gethostname(ptr long) WS_gethostname
58 stub WSAJoinLeaf
59 stub WSALookupServiceBeginA
60 stub WSALookupServiceBeginW
......
......@@ -4,10 +4,20 @@
* Copyright (C) 2001 Stefan Leichter
*/
/* All we need are a couple constants for EnumProtocols. Once it is
* moved to ws2_32 we may no longer need it
*/
#define USE_WS_PREFIX
#include "config.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include "winbase.h"
#include "winnls.h"
#include "wine/unicode.h"
......@@ -52,12 +62,12 @@ static INT WSOCK32_EnterSingleProtocol( INT iProtocol,
*lpSize = sizeof( PROTOCOL_INFOA);
switch (iProtocol) {
case IPPROTO_TCP :
case WS_IPPROTO_TCP :
dwLength = (unicode) ? sizeof(WCHAR) * (strlenW(NameTcp)+1) :
WideCharToMultiByte( CP_ACP, 0, NameTcp, -1,
NULL, 0, NULL, NULL);
break;
case IPPROTO_UDP :
case WS_IPPROTO_UDP :
dwLength = (unicode) ? sizeof(WCHAR) * (strlenW(NameUdp)+1) :
WideCharToMultiByte( CP_ACP, 0, NameUdp, -1,
NULL, 0, NULL, NULL);
......@@ -97,7 +107,7 @@ static INT WSOCK32_EnterSingleProtocol( INT iProtocol,
lpBuffer->iProtocol = iProtocol;
switch (iProtocol) {
case IPPROTO_TCP :
case WS_IPPROTO_TCP :
lpBuffer->dwServiceFlags = XP_FRAGMENTATION | XP_EXPEDITED_DATA |
XP_GRACEFUL_CLOSE | XP_GUARANTEED_ORDER |
XP_GUARANTEED_DELIVERY;
......@@ -108,7 +118,7 @@ static INT WSOCK32_EnterSingleProtocol( INT iProtocol,
lpBuffer->dwMessageSize = 0;
lpProtName = NameTcp;
break;
case IPPROTO_UDP :
case WS_IPPROTO_UDP :
lpBuffer->dwServiceFlags = XP_FRAGMENTATION | XP_SUPPORTS_BROADCAST |
XP_SUPPORTS_MULTICAST | XP_MESSAGE_ORIENTED |
XP_CONNECTIONLESS;
......@@ -162,6 +172,10 @@ static INT WSOCK32_EnterSingleProtocol( INT iProtocol,
return iAnz;
}
/* FIXME: EnumProtocols should be moved to winsock2, and this should be
* implemented by calling out to WSAEnumProtocols. See:
* http://support.microsoft.com/support/kb/articles/Q129/3/15.asp
*/
/*****************************************************************************
* WSOCK32_EnumProtocol [internal]
*
......@@ -194,7 +208,7 @@ static INT WSOCK32_EnumProtocol( LPINT lpiProtocols, PROTOCOL_INFOA* lpBuffer,
LPDWORD lpdwLength, BOOL unicode)
{ DWORD dwCurSize, dwOldSize = *lpdwLength, dwTemp;
INT anz = 0, i;
INT iLocal[] = { IPPROTO_TCP, IPPROTO_UDP, NSPROTO_IPX, NSPROTO_SPXII, 0};
INT iLocal[] = { WS_IPPROTO_TCP, WS_IPPROTO_UDP, NSPROTO_IPX, NSPROTO_SPXII, 0};
if (!lpiProtocols) lpiProtocols = iLocal;
......
......@@ -5,17 +5,23 @@
*/
/*
FIXME: This hack is fixing a problem in WsControl. When we call socket(),
it is supposed to call into ws2_32's WSOCK32_socket.
The problem is that socket() is predefined in a linux system header that
we are including, which is different from the WINE definition.
(cdecl vs. stdapi) The result is stack corruption.
The correct answer to this problem is to make winsock.h not dependent
on system headers, that way all of our functions are defined consistently.
Until that happens we need this hack.
*/
/* FIXME: This hack is fixing a problem in WsControl. When we call socket(),
* it will call into ws2_32's WSOCK32_socket (because of the redirection in
* our own .spec file).
* The problem is that socket() is predefined in a linux system header that
* we are including, which is different from the WINE definition.
* (cdecl vs. stdapi). The result is stack corruption.
* Furthermore WsControl uses Unix macros and types. This forces us to include
* the Unix headers which then conflict with the winsock headers. This forces
* us to use USE_WS_PREFIX but then ioctlsocket is called WS_ioctlsocket,
* which causes link problems. The correct solution is to implement
* WsControl using calls to WSAIoctl. Then we should no longer need to use the
* Unix headers. This would also have the advantage of reducing code
* duplication.
* Until that happens we need this ugly hack.
*/
#define USE_WS_PREFIX
#define socket linux_socket
#define recv linux_recv
/* */
......@@ -31,6 +37,7 @@
#include "winnt.h"
#include "wscontrol.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
......@@ -48,11 +55,15 @@
#endif
/* FIXME: The rest of the socket() cdecl<->stdapi stack corruption problem
discussed above. */
* discussed above.
*/
#undef socket
#undef recv
extern SOCKET WINAPI socket(INT af, INT type, INT protocol);
extern SOCKET WINAPI recv(SOCKET,char*,int,int);
/* Plus some missing prototypes, due to the WS_ prefixing */
extern int WINAPI closesocket(SOCKET);
extern int WINAPI ioctlsocket(SOCKET,long,u_long*);
/* */
......@@ -199,7 +210,7 @@ DWORD WINAPI WsControl(DWORD protocoll,
}
/* Get a socket so that we can use ioctl */
if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
if ( (sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
ERR ("Error creating socket!\n");
return (-1);
......@@ -365,7 +376,7 @@ DWORD WINAPI WsControl(DWORD protocoll,
/* Get a socket so we can use ioctl */
if ( (sock = socket (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
if ( (sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
ERR ("Error creating socket!\n");
return (-1);
......@@ -380,13 +391,13 @@ DWORD WINAPI WsControl(DWORD protocoll,
/* IP Address */
strcpy (ifInfo.ifr_name, ifName);
ifInfo.ifr_addr.sa_family = AF_INET;
if (ioctlsocket(sock, SIOCGIFADDR, (ULONG*)&ifInfo) < 0)
if (ioctlsocket(sock, SIOCGIFADDR, (ULONG*)&ifInfo) < 0)
{
baseIPInfo->iae_addr = 0x0;
}
else
{
struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
struct WS_sockaddr_in* ipTemp = (struct WS_sockaddr_in*)&ifInfo.ifr_addr;
baseIPInfo->iae_addr = ipTemp->sin_addr.S_un.S_addr;
}
......@@ -398,8 +409,8 @@ DWORD WINAPI WsControl(DWORD protocoll,
}
else
{
struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_broadaddr;
baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr;
struct WS_sockaddr_in* ipTemp = (struct WS_sockaddr_in*)&ifInfo.ifr_broadaddr;
baseIPInfo->iae_bcastaddr = ipTemp->sin_addr.S_un.S_addr;
}
/* Subnet Mask */
......@@ -417,12 +428,12 @@ DWORD WINAPI WsControl(DWORD protocoll,
baseIPInfo->iae_mask = 0;
ERR ("Unable to determine Netmask on your platform!\n");
#else
struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_addr;
baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
struct WS_sockaddr_in* ipTemp = (struct WS_sockaddr_in*)&ifInfo.ifr_addr;
baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
#endif
#else
struct ws_sockaddr_in *ipTemp = (struct ws_sockaddr_in *)&ifInfo.ifr_netmask;
baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
#else
struct WS_sockaddr_in* ipTemp = (struct WS_sockaddr_in*)&ifInfo.ifr_netmask;
baseIPInfo->iae_mask = ipTemp->sin_addr.S_un.S_addr;
#endif
}
......@@ -959,7 +970,7 @@ INT WINAPI WSARecvEx(SOCKET s, char *buf, INT len, INT *flags)
/***********************************************************************
* s_perror (WSOCK32.1108)
*/
void WINAPI WS_s_perror(LPCSTR message)
void WINAPI s_perror(LPCSTR message)
{
FIXME("(%s): stub\n",message);
return;
......
......@@ -68,7 +68,7 @@ debug_channels (winsock)
#1105 stub sethostname
#1106 stub dn_expand
1107 stdcall WSARecvEx(long ptr long ptr) WSARecvEx
1108 stdcall s_perror(str) WS_s_perror
1108 stdcall s_perror(str) s_perror
1109 stub GetAddressByNameA
1110 stub GetAddressByNameW
1111 stdcall EnumProtocolsA(ptr ptr ptr) EnumProtocolsA
......
......@@ -188,6 +188,7 @@ INSTALLED_INCLUDES = \
wnaspi32.h \
wownt32.h \
ws2spi.h \
ws2tcpip.h \
wshisotp.h \
wsipx.h \
wtypes.h \
......
......@@ -35,8 +35,10 @@
#include "shellapi.h"
/* #include "winperf.h" */
#ifndef WINE_NOWINSOCK
#include "winsock2.h"
/* #include "mswsock.h" */
#endif /* WINE_NOWINSOCK */
#ifndef NOCRYPT
#include "wincrypt.h"
......
......@@ -60,7 +60,7 @@ typedef struct WSAData16
#include "poppack.h"
#define WS_FD_CLR16(fd, set) __WS_FD_CLR((fd),(set), ws_fd_set16)
#define WS_FD_SET16(fd, set) __WS_FD_SET((fd),(set), ws_fd_set16)
#define WS_FD_SET16(fd, set) __WS_FD_SET1((fd),(set), ws_fd_set16)
#define WS_FD_ZERO16(set) (((ws_fd_set16*)(set))->fd_count=0)
#define WS_FD_ISSET16(fd, set) __WSAFDIsSet16((SOCKET16)(fd), (ws_fd_set16*)(set))
......
#ifndef __WS2TCPIP__
#define __WS2TCPIP__
#ifdef USE_WS_PREFIX
#define WS(x) WS_##x
#else
#define WS(x) x
#endif
/* FIXME: This gets defined by some Unix (Linux) header and messes things */
#undef s6_addr
typedef struct WS(in_addr6)
{
u_char s6_addr[16]; /* IPv6 address */
} IN6_ADDR, *PIN6_ADDR, *LPIN6_ADDR;
typedef struct WS(sockaddr_in6)
{
short sin6_family; /* AF_INET6 */
u_short sin6_port; /* Transport level port number */
u_long sin6_flowinfo; /* IPv6 flow information */
struct WS(in_addr6) sin6_addr; /* IPv6 address */
} SOCKADDR_IN6,*PSOCKADDR_IN6, *LPSOCKADDR_IN6;
typedef union sockaddr_gen
{
struct WS(sockaddr) Address;
struct WS(sockaddr_in) AddressIn;
struct WS(sockaddr_in6) AddressIn6;
} WS(sockaddr_gen);
/* Structure to keep interface specific information */
typedef struct _INTERFACE_INFO
{
u_long iiFlags; /* Interface flags */
WS(sockaddr_gen) iiAddress; /* Interface address */
WS(sockaddr_gen) iiBroadcastAddress; /* Broadcast address */
WS(sockaddr_gen) iiNetmask; /* Network mask */
} INTERFACE_INFO, * LPINTERFACE_INFO;
/* Possible flags for the iiFlags - bitmask */
#ifndef USE_WS_PREFIX
#define IFF_UP 0x00000001 /* Interface is up */
#define IFF_BROADCAST 0x00000002 /* Broadcast is supported */
#define IFF_LOOPBACK 0x00000004 /* this is loopback interface */
#define IFF_POINTTOPOINT 0x00000008 /* this is point-to-point interface */
#define IFF_MULTICAST 0x00000010 /* multicast is supported */
#else
#define WS_IFF_UP 0x00000001 /* Interface is up */
#define WS_IFF_BROADCAST 0x00000002 /* Broadcast is supported */
#define WS_IFF_LOOPBACK 0x00000004 /* this is loopback interface */
#define WS_IFF_POINTTOPOINT 0x00000008 /* this is point-to-point interface */
#define WS_IFF_MULTICAST 0x00000010 /* multicast is supported */
#endif /* USE_WS_PREFIX */
#endif /* __WS2TCPIP__ */
/* WCIPX.H
/* WSIPX.H
*/
#ifndef _WINE_WSIPX_
#define _WINE_WSIPX_
#ifdef __cplusplus
extern "C" {
#endif /* defined(__cplusplus) */
#ifdef USE_WS_PREFIX
# define WS(x) WS_##x
#else
# define WS(x) x
#endif
typedef struct ws_sockaddr_ipx
typedef struct WS_sockaddr_ipx
{
short sa_family;
char sa_netnum[4];
......@@ -19,13 +21,9 @@ typedef struct ws_sockaddr_ipx
/*
* constants
*/
#define NSPROTO_IPX 1000
#define NSPROTO_SPX 1256
#define NSPROTO_SPXII 1257
#ifdef __cplusplus
} /* extern "C" */
#endif /* defined(__cplusplus) */
#define NSPROTO_IPX 1000
#define NSPROTO_SPX 1256
#define NSPROTO_SPXII 1257
#undef WS
#endif /* _WINE_WSIPX_ */
......@@ -32,12 +32,17 @@
#include "winerror.h"
#include "winbase.h"
#include "winsock2.h"
#include "process.h"
#include "handle.h"
#include "thread.h"
#include "request.h"
/* To avoid conflicts with the Unix socket headers. Plus we only need a few
* macros anyway.
*/
#define USE_WS_PREFIX
#include "winsock2.h"
struct sock
{
struct object obj; /* object header */
......
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