Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
c5e856a3
Commit
c5e856a3
authored
Jun 16, 2003
by
Juan Lang
Committed by
Alexandre Julliard
Jun 16, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use iphlpapi to enumerate LAN adapters.
Add names to NetBIOS transports, and eliminates loopback adapters from enumerated LAN adapters.
parent
9d5295f5
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
133 additions
and
162 deletions
+133
-162
Makefile.in
dlls/netapi32/Makefile.in
+1
-1
netapi32.c
dlls/netapi32/netapi32.c
+50
-152
wksta.c
dlls/netapi32/wksta.c
+80
-9
nb30.h
include/nb30.h
+2
-0
No files found.
dlls/netapi32/Makefile.in
View file @
c5e856a3
...
@@ -4,7 +4,7 @@ TOPOBJDIR = ../..
...
@@ -4,7 +4,7 @@ TOPOBJDIR = ../..
SRCDIR
=
@srcdir@
SRCDIR
=
@srcdir@
VPATH
=
@srcdir@
VPATH
=
@srcdir@
MODULE
=
netapi32.dll
MODULE
=
netapi32.dll
IMPORTS
=
advapi32 kernel32
IMPORTS
=
iphlpapi
advapi32 kernel32
LDDLLFLAGS
=
@LDDLLFLAGS@
LDDLLFLAGS
=
@LDDLLFLAGS@
SYMBOLFILE
=
$(MODULE)
.tmp.o
SYMBOLFILE
=
$(MODULE)
.tmp.o
...
...
dlls/netapi32/netapi32.c
View file @
c5e856a3
...
@@ -20,6 +20,7 @@
...
@@ -20,6 +20,7 @@
#include <string.h>
#include <string.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#ifdef HAVE_UNISTD_H
# include <unistd.h>
# include <unistd.h>
...
@@ -33,183 +34,73 @@
...
@@ -33,183 +34,73 @@
#include "winerror.h"
#include "winerror.h"
#include "nb30.h"
#include "nb30.h"
#include "lmcons.h"
#include "lmcons.h"
#include "iphlpapi.h"
#ifdef HAVE_SYS_FILE_H
# include <sys/file.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#ifdef HAVE_NET_IF_H
# include <net/if.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef max
# define max(a,b) ((a) > (b) ? (a) : (b))
# endif
# define ifreq_size(i) max(sizeof(struct ifreq),\
sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
# else
# define ifreq_size(i) sizeof(struct ifreq)
# endif
/* HAVE_STRUCT_SOCKADDR_SA_LEN */
WINE_DEFAULT_DEBUG_CHANNEL
(
netbios
);
WINE_DEFAULT_DEBUG_CHANNEL
(
netbios
);
HMODULE
NETAPI32_hModule
=
0
;
HMODULE
NETAPI32_hModule
=
0
;
struct
NetBiosAdapter
{
int
valid
;
unsigned
char
address
[
6
];
};
static
struct
NetBiosAdapter
NETBIOS_Adapter
[
MAX_LANA
];
# ifdef SIOCGIFHWADDR
int
get_hw_address
(
int
sd
,
struct
ifreq
*
ifr
,
unsigned
char
*
address
)
{
if
(
ioctl
(
sd
,
SIOCGIFHWADDR
,
ifr
)
<
0
)
return
-
1
;
memcpy
(
address
,
(
unsigned
char
*
)
&
ifr
->
ifr_hwaddr
.
sa_data
,
6
);
return
0
;
}
# else
# ifdef SIOCGENADDR
int
get_hw_address
(
int
sd
,
struct
ifreq
*
ifr
,
unsigned
char
*
address
)
{
if
(
ioctl
(
sd
,
SIOCGENADDR
,
ifr
)
<
0
)
return
-
1
;
memcpy
(
address
,
(
unsigned
char
*
)
ifr
->
ifr_enaddr
,
6
);
return
0
;
}
# else
int
get_hw_address
(
int
sd
,
struct
ifreq
*
ifr
,
unsigned
char
*
address
)
{
return
-
1
;
}
# endif
/* SIOCGENADDR */
# endif
/* SIOCGIFHWADDR */
int
enum_hw
(
void
)
{
int
ret
=
0
;
#ifdef HAVE_NET_IF_H
int
sd
;
struct
ifreq
ifr
,
*
ifrp
;
struct
ifconf
ifc
;
unsigned
char
buf
[
1024
];
int
i
,
ofs
;
/* BSD 4.4 defines the size of an ifreq to be
* max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
* However, under earlier systems, sa_len isn't present, so
* the size is just sizeof(struct ifreq)
*/
sd
=
socket
(
AF_INET
,
SOCK_DGRAM
,
IPPROTO_IP
);
if
(
sd
<
0
)
return
NRC_OPENERROR
;
memset
(
buf
,
0
,
sizeof
(
buf
));
ifc
.
ifc_len
=
sizeof
(
buf
);
ifc
.
ifc_buf
=
buf
;
/* get the ifconf interface */
if
(
ioctl
(
sd
,
SIOCGIFCONF
,
(
char
*
)
&
ifc
)
<
0
)
{
close
(
sd
);
return
NRC_OPENERROR
;
}
/* loop through the interfaces, looking for a valid one */
/* n = ifc.ifc_len; */
ofs
=
0
;
for
(
i
=
0
;
i
<
ifc
.
ifc_len
;
i
++
)
{
unsigned
char
*
a
=
NETBIOS_Adapter
[
i
].
address
;
ifrp
=
(
struct
ifreq
*
)((
char
*
)
ifc
.
ifc_buf
+
ofs
);
strncpy
(
ifr
.
ifr_name
,
ifrp
->
ifr_name
,
IFNAMSIZ
);
/* try to get the address for this interface */
if
(
get_hw_address
(
sd
,
&
ifr
,
a
)
==
0
)
{
/* make sure it's not blank */
/* if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
continue; */
TRACE
(
"Found valid adapter %d at %02x:%02x:%02x:%02x:%02x:%02x
\n
"
,
i
,
a
[
0
],
a
[
1
],
a
[
2
],
a
[
3
],
a
[
4
],
a
[
5
]);
NETBIOS_Adapter
[
i
].
valid
=
TRUE
;
ret
++
;
}
ofs
+=
ifreq_size
(
ifr
);
}
close
(
sd
);
TRACE
(
"found %d adapters
\n
"
,
ret
);
#endif
/* HAVE_NET_IF_H */
return
ret
;
}
void
wprint_mac
(
WCHAR
*
buffer
,
int
index
)
{
int
i
;
unsigned
char
val
;
for
(
i
=
0
;
i
<
6
;
i
++
)
{
val
=
NETBIOS_Adapter
[
index
].
address
[
i
];
if
((
val
>>
4
)
>
9
)
buffer
[
2
*
i
]
=
(
WCHAR
)((
val
>>
4
)
+
'A'
-
10
);
else
buffer
[
2
*
i
]
=
(
WCHAR
)((
val
>>
4
)
+
'0'
);
if
((
val
&
0xf
)
>
9
)
buffer
[
2
*
i
+
1
]
=
(
WCHAR
)((
val
&
0xf
)
+
'A'
-
10
);
else
buffer
[
2
*
i
+
1
]
=
(
WCHAR
)((
val
&
0xf
)
+
'0'
);
}
buffer
[
12
]
=
(
WCHAR
)
0
;
}
static
UCHAR
NETBIOS_Enum
(
PNCB
ncb
)
static
UCHAR
NETBIOS_Enum
(
PNCB
ncb
)
{
{
int
i
;
int
i
;
LANA_ENUM
*
lanas
=
(
PLANA_ENUM
)
ncb
->
ncb_buffer
;
LANA_ENUM
*
lanas
=
(
PLANA_ENUM
)
ncb
->
ncb_buffer
;
DWORD
apiReturn
,
size
=
0
;
PMIB_IFTABLE
table
;
UCHAR
ret
;
TRACE
(
"NCBENUM
\n
"
);
TRACE
(
"NCBENUM
\n
"
);
apiReturn
=
GetIfTable
(
NULL
,
&
size
,
FALSE
);
if
(
apiReturn
!=
NO_ERROR
)
{
table
=
(
PMIB_IFTABLE
)
malloc
(
size
);
if
(
table
)
{
apiReturn
=
GetIfTable
(
table
,
&
size
,
FALSE
);
if
(
apiReturn
==
NO_ERROR
)
{
lanas
->
length
=
0
;
lanas
->
length
=
0
;
for
(
i
=
0
;
i
<
enum_hw
();
i
++
)
for
(
i
=
0
;
i
<
table
->
dwNumEntries
&&
lanas
->
length
<
MAX_LANA
;
i
++
)
{
if
(
table
->
table
[
i
].
dwType
!=
MIB_IF_TYPE_LOOPBACK
)
{
{
lanas
->
lana
[
lanas
->
length
]
=
i
;
lanas
->
lana
[
lanas
->
length
]
=
table
->
table
[
i
].
dwIndex
;
lanas
->
length
++
;
lanas
->
length
++
;
}
}
return
NRC_GOODRET
;
}
ret
=
NRC_GOODRET
;
}
else
ret
=
NRC_SYSTEM
;
free
(
table
);
}
else
ret
=
NRC_NORESOURCES
;
}
else
ret
=
NRC_SYSTEM
;
return
ret
;
}
}
static
UCHAR
NETBIOS_Astat
(
PNCB
ncb
)
static
UCHAR
NETBIOS_Astat
(
PNCB
ncb
)
{
{
struct
NetBiosAdapter
*
nad
=
&
NETBIOS_Adapter
[
ncb
->
ncb_lana_num
];
PADAPTER_STATUS
astat
=
(
PADAPTER_STATUS
)
ncb
->
ncb_buffer
;
PADAPTER_STATUS
astat
=
(
PADAPTER_STATUS
)
ncb
->
ncb_buffer
;
MIB_IFROW
row
;
TRACE
(
"NCBASTAT (Adapter %d)
\n
"
,
ncb
->
ncb_lana_num
);
TRACE
(
"NCBASTAT (Adapter %d)
\n
"
,
ncb
->
ncb_lana_num
);
if
(
!
nad
->
valid
)
return
NRC_INVADDRESS
;
memset
(
astat
,
0
,
sizeof
astat
);
memset
(
astat
,
0
,
sizeof
astat
);
memcpy
(
astat
->
adapter_address
,
nad
->
address
,
sizeof
astat
->
adapter_address
);
row
.
dwIndex
=
ncb
->
ncb_lana_num
;
if
(
GetIfEntry
(
&
row
)
!=
NO_ERROR
)
return
NRC_INVADDRESS
;
/* doubt anyone cares, but why not.. */
if
(
row
.
dwType
==
MIB_IF_TYPE_TOKENRING
)
astat
->
adapter_type
=
0xff
;
else
astat
->
adapter_type
=
0xfe
;
/* for Ethernet */
return
NRC_GOODRET
;
return
NRC_GOODRET
;
}
}
...
@@ -241,11 +132,18 @@ BOOL WINAPI Netbios(PNCB pncb)
...
@@ -241,11 +132,18 @@ BOOL WINAPI Netbios(PNCB pncb)
{
{
case
NCBRESET
:
case
NCBRESET
:
FIXME
(
"NCBRESET adapter %d
\n
"
,
pncb
->
ncb_lana_num
);
FIXME
(
"NCBRESET adapter %d
\n
"
,
pncb
->
ncb_lana_num
);
if
(
(
pncb
->
ncb_lana_num
<
MAX_LANA
)
&&
if
(
pncb
->
ncb_lana_num
<
MAX_LANA
)
NETBIOS_Adapter
[
pncb
->
ncb_lana_num
].
valid
)
{
MIB_IFROW
row
;
row
.
dwIndex
=
pncb
->
ncb_lana_num
;
if
(
GetIfEntry
(
&
row
)
!=
NO_ERROR
)
ret
=
NRC_GOODRET
;
ret
=
NRC_GOODRET
;
else
else
ret
=
NRC_ILLCMD
;
/* NetBIOS emulator not found */
ret
=
NRC_ILLCMD
;
/* NetBIOS emulator not found */
}
else
ret
=
NRC_ILLCMD
;
/* NetBIOS emulator not found */
break
;
break
;
case
NCBADDNAME
:
case
NCBADDNAME
:
...
...
dlls/netapi32/wksta.c
View file @
c5e856a3
...
@@ -18,12 +18,14 @@
...
@@ -18,12 +18,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
*/
#include <stdlib.h>
#include "winbase.h"
#include "winbase.h"
#include "nb30.h"
#include "nb30.h"
#include "lmcons.h"
#include "lmcons.h"
#include "lmapibuf.h"
#include "lmapibuf.h"
#include "lmerr.h"
#include "lmerr.h"
#include "lmwksta.h"
#include "lmwksta.h"
#include "iphlpapi.h"
#include "winerror.h"
#include "winerror.h"
#include "winternl.h"
#include "winternl.h"
#include "ntsecapi.h"
#include "ntsecapi.h"
...
@@ -59,8 +61,53 @@ BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
...
@@ -59,8 +61,53 @@ BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
}
}
}
}
int
enum_hw
(
void
);
static
void
wprint_mac
(
WCHAR
*
buffer
,
PIP_ADAPTER_INFO
adapter
)
void
wprint_mac
(
WCHAR
*
buffer
,
int
index
);
{
if
(
adapter
!=
NULL
)
{
int
i
;
unsigned
char
val
;
for
(
i
=
0
;
i
<
max
(
adapter
->
AddressLength
,
6
);
i
++
)
{
val
=
adapter
->
Address
[
i
];
if
((
val
>>
4
)
>
9
)
buffer
[
2
*
i
]
=
(
WCHAR
)((
val
>>
4
)
+
'A'
-
10
);
else
buffer
[
2
*
i
]
=
(
WCHAR
)((
val
>>
4
)
+
'0'
);
if
((
val
&
0xf
)
>
9
)
buffer
[
2
*
i
+
1
]
=
(
WCHAR
)((
val
&
0xf
)
+
'A'
-
10
);
else
buffer
[
2
*
i
+
1
]
=
(
WCHAR
)((
val
&
0xf
)
+
'0'
);
}
buffer
[
12
]
=
(
WCHAR
)
0
;
}
else
buffer
[
0
]
=
0
;
}
#define TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
#define TRANSPORT_NAME_LEN \
(sizeof(TRANSPORT_NAME_HEADER) + MAX_ADAPTER_NAME_LENGTH)
static
void
wprint_name
(
WCHAR
*
buffer
,
int
len
,
PIP_ADAPTER_INFO
adapter
)
{
WCHAR
*
ptr
;
const
char
*
name
;
if
(
!
buffer
)
return
;
if
(
!
adapter
)
return
;
for
(
ptr
=
buffer
,
name
=
TRANSPORT_NAME_HEADER
;
*
name
&&
ptr
<
buffer
+
len
;
ptr
++
,
name
++
)
*
ptr
=
*
name
;
for
(
name
=
adapter
->
AdapterName
;
name
&&
*
name
&&
ptr
<
buffer
+
len
;
ptr
++
,
name
++
)
*
ptr
=
*
name
;
*
ptr
=
'\0'
;
}
NET_API_STATUS
WINAPI
NET_API_STATUS
WINAPI
NetWkstaTransportEnum
(
LPCWSTR
ServerName
,
DWORD
level
,
LPBYTE
*
pbuf
,
NetWkstaTransportEnum
(
LPCWSTR
ServerName
,
DWORD
level
,
LPBYTE
*
pbuf
,
...
@@ -87,43 +134,67 @@ NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
...
@@ -87,43 +134,67 @@ NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
case
0
:
/* transport info */
case
0
:
/* transport info */
{
{
PWKSTA_TRANSPORT_INFO_0
ti
;
PWKSTA_TRANSPORT_INFO_0
ti
;
int
i
,
size_needed
,
n_adapt
=
enum_hw
();
int
i
,
size_needed
,
n_adapt
;
DWORD
apiReturn
,
adaptInfoSize
=
0
;
PIP_ADAPTER_INFO
info
,
ptr
;
if
(
n_adapt
==
0
)
apiReturn
=
GetAdaptersInfo
(
NULL
,
&
adaptInfoSize
);
if
(
apiReturn
==
ERROR_NO_DATA
)
return
ERROR_NETWORK_UNREACHABLE
;
return
ERROR_NETWORK_UNREACHABLE
;
if
(
!
read_entries
)
if
(
!
read_entries
)
return
STATUS_ACCESS_VIOLATION
;
return
STATUS_ACCESS_VIOLATION
;
if
(
!
total_entries
||
!
pbuf
)
if
(
!
total_entries
||
!
pbuf
)
return
RPC_X_NULL_REF_POINTER
;
return
RPC_X_NULL_REF_POINTER
;
info
=
(
PIP_ADAPTER_INFO
)
malloc
(
adaptInfoSize
);
apiReturn
=
GetAdaptersInfo
(
info
,
&
adaptInfoSize
);
if
(
apiReturn
!=
NO_ERROR
)
{
free
(
info
);
return
apiReturn
;
}
for
(
n_adapt
=
0
,
ptr
=
info
;
ptr
;
ptr
=
ptr
->
Next
)
n_adapt
++
;
size_needed
=
n_adapt
*
(
sizeof
(
WKSTA_TRANSPORT_INFO_0
)
size_needed
=
n_adapt
*
(
sizeof
(
WKSTA_TRANSPORT_INFO_0
)
*
13
*
sizeof
(
WCHAR
));
+
n_adapt
*
TRANSPORT_NAME_LEN
*
sizeof
(
WCHAR
)
+
n_adapt
*
13
*
sizeof
(
WCHAR
));
if
(
prefmaxlen
==
MAX_PREFERRED_LENGTH
)
if
(
prefmaxlen
==
MAX_PREFERRED_LENGTH
)
NetApiBufferAllocate
(
size_needed
,
(
LPVOID
*
)
pbuf
);
NetApiBufferAllocate
(
size_needed
,
(
LPVOID
*
)
pbuf
);
else
else
{
{
if
(
size_needed
>
prefmaxlen
)
if
(
size_needed
>
prefmaxlen
)
{
free
(
info
);
return
ERROR_MORE_DATA
;
return
ERROR_MORE_DATA
;
}
NetApiBufferAllocate
(
prefmaxlen
,
NetApiBufferAllocate
(
prefmaxlen
,
(
LPVOID
*
)
pbuf
);
(
LPVOID
*
)
pbuf
);
}
}
for
(
i
=
0
;
i
<
n_adapt
;
i
++
)
for
(
i
=
0
,
ptr
=
info
;
ptr
;
ptr
=
ptr
->
Next
,
i
++
)
{
{
ti
=
(
PWKSTA_TRANSPORT_INFO_0
)
ti
=
(
PWKSTA_TRANSPORT_INFO_0
)
((
PBYTE
)
*
pbuf
+
i
*
sizeof
(
WKSTA_TRANSPORT_INFO_0
));
((
PBYTE
)
*
pbuf
+
i
*
sizeof
(
WKSTA_TRANSPORT_INFO_0
));
ti
->
wkti0_quality_of_service
=
0
;
ti
->
wkti0_quality_of_service
=
0
;
ti
->
wkti0_number_of_vcs
=
0
;
ti
->
wkti0_number_of_vcs
=
0
;
ti
->
wkti0_transport_name
=
NULL
;
ti
->
wkti0_transport_name
=
(
LPWSTR
)
((
PBYTE
)
*
pbuf
+
n_adapt
*
sizeof
(
WKSTA_TRANSPORT_INFO_0
)
+
i
*
TRANSPORT_NAME_LEN
*
sizeof
(
WCHAR
));
wprint_name
(
ti
->
wkti0_transport_name
,
TRANSPORT_NAME_LEN
,
ptr
);
ti
->
wkti0_transport_address
=
(
LPWSTR
)
ti
->
wkti0_transport_address
=
(
LPWSTR
)
((
PBYTE
)
*
pbuf
+
n_adapt
*
sizeof
(
WKSTA_TRANSPORT_INFO_0
)
((
PBYTE
)
*
pbuf
+
n_adapt
*
sizeof
(
WKSTA_TRANSPORT_INFO_0
)
+
n_adapt
*
TRANSPORT_NAME_LEN
*
sizeof
(
WCHAR
)
+
i
*
13
*
sizeof
(
WCHAR
));
+
i
*
13
*
sizeof
(
WCHAR
));
ti
->
wkti0_wan_ish
=
TRUE
;
/*TCPIP/NETBIOS Protocoll*/
ti
->
wkti0_wan_ish
=
TRUE
;
/*TCPIP/NETBIOS Protocoll*/
wprint_mac
(
ti
->
wkti0_transport_address
,
i
);
wprint_mac
(
ti
->
wkti0_transport_address
,
ptr
);
TRACE
(
"%d of %d:ti at %p transport_address at %p %s
\n
"
,
i
,
n_adapt
,
TRACE
(
"%d of %d:ti at %p transport_address at %p %s
\n
"
,
i
,
n_adapt
,
ti
,
ti
->
wkti0_transport_address
,
debugstr_w
(
ti
->
wkti0_transport_address
));
ti
,
ti
->
wkti0_transport_address
,
debugstr_w
(
ti
->
wkti0_transport_address
));
}
}
*
read_entries
=
n_adapt
;
*
read_entries
=
n_adapt
;
*
total_entries
=
n_adapt
;
*
total_entries
=
n_adapt
;
free
(
info
);
if
(
hresume
)
*
hresume
=
0
;
if
(
hresume
)
*
hresume
=
0
;
break
;
break
;
}
}
...
...
include/nb30.h
View file @
c5e856a3
...
@@ -100,9 +100,11 @@ typedef struct _LANA_ENUM
...
@@ -100,9 +100,11 @@ typedef struct _LANA_ENUM
#define NRC_ILLCMD 0x03
#define NRC_ILLCMD 0x03
#define NRC_CMDTMO 0x05
#define NRC_CMDTMO 0x05
#define NRC_INCOMP 0x06
#define NRC_INCOMP 0x06
#define NRC_NORESOURCES 0x38
#define NRC_INVADDRESS 0x39
#define NRC_INVADDRESS 0x39
#define NRC_PENDING 0xff
#define NRC_PENDING 0xff
#define NRC_OPENERROR 0x3f
#define NRC_OPENERROR 0x3f
#define NRC_SYSTEM 0x40
#ifdef __cplusplus
#ifdef __cplusplus
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment