 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Mon May 19, 2008 9:09 am Post subject: |
|
|
Try this:
| Code: | ; s = socket
VarSetCapacity(sockaddr, 16)
DllCall("Ws2_32\getpeername", "uint", s, "uint", &sockaddr, "int*", 16)
peername := DllCall("Ws2_32\inet_ntoa", "uint", &sockaddr+4, "str") ; address
. ":" NumGet(sockaddr,2,"ushort") ; :port | Untested. |
|
| Back to top |
|
 |
jmanx
Joined: 14 May 2008 Posts: 40
|
Posted: Mon May 19, 2008 6:49 pm Post subject: |
|
|
I tried to put in that code for the getpeername. It gets a random address everytime they connect.
| Code: | i=1
Loop ; Wait for incomming connections
{
; accept requests that are in the pipeline of the socket
conectioncheck%i% := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
; Ws2_22/accept returns the new Connection-Socket if a connection request was in the pipeline
; on failure it returns an negative value
if conectioncheck%i% > 1
{
i++
; s = socket
VarSetCapacity(sockaddr, 16)
DllCall("Ws2_32\getpeername", "uint", s, "uint", &sockaddr, "int*", 16)
peername := DllCall("Ws2_32\inet_ntoa", "uint", &sockaddr+4, "str") ; address
. ":" NumGet(sockaddr,2,"ushort") ; :port
msgbox, %peername%
}
sleep 500 ; wait half 1 second then accept again
}
return |
|
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Mon May 19, 2008 9:54 pm Post subject: |
|
|
Oops, inet_ntoa accepts an in_addr structure, not a pointer to one:
| Code: | ; s = socket
VarSetCapacity(sockaddr, 16, 0)
DllCall("Ws2_32\getpeername", "uint", s, "uint", &sockaddr, "int*", 16)
peername := DllCall("Ws2_32\inet_ntoa", "uint", NumGet(sockaddr,4), "str")
. ":" NumGet(sockaddr,2,"ushort")
msgbox %peername% |
|
|
| Back to top |
|
 |
jmanx
Joined: 14 May 2008 Posts: 40
|
Posted: Tue May 20, 2008 12:53 am Post subject: |
|
|
| it just says 0.0.0.0:0 now |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Tue May 20, 2008 7:09 am Post subject: |
|
|
Have you changed 's' to 'socket'?  |
|
| Back to top |
|
 |
jmanx
Joined: 14 May 2008 Posts: 40
|
Posted: Tue May 20, 2008 9:03 am Post subject: |
|
|
| yes i did |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Tue May 20, 2008 12:10 pm Post subject: |
|
|
| In that case, post your code. I tested my code with AHK Remote, and it worked perfectly. |
|
| Back to top |
|
 |
jmanx
Joined: 14 May 2008 Posts: 40
|
Posted: Wed May 21, 2008 3:45 am Post subject: |
|
|
Client:
| Code: | #singleinstance force
;#notrayicon
; -------------------------------------------------
;-----------CLIENTSCRIPT----------------------
; -------------------------------------------------
; CONFIGURATION SECTION:
; Specify address and port of the server.
c=0
con=0
end=0
ClientName=%A_Computername%
Network_Address = 68.58.121.5
Network_Port = 1000
; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------
Gui, Add, Edit, x6 y5 w300 h140 vchat,
Gui, Add, Edit, x6 y155 w300 h20 vSendText,
Gui, Add, Edit, x6 y185 w130 h20 vClientName, %ClientName%
Gui, Add, Button, x146 y185 w80 h20 gSendviaNet, Send
Gui, Add, Button, x236 y185 w70 h20 vconnect gConnection_Init, Connect
Gui, Show, x336 y194 h214 w315, Client
WinGet Gui_ID, ID, A
return
$Enter::
ifwinactive, Client
{
goto, SendviaNet
}
else
send, {ENTER}
return
Connection_Init:
OnExit, ExitSub ; For connection cleanup purposes.
; Connect to any type of server:
socket := ConnectToAddress(Network_Address, Network_Port)
if socket = -1 ; Connection failed (it already displayed the reason).
ExitApp
; Find this script's main window:
Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off
; When the OS notifies the script that there is incoming data waiting to be received,
; the following causes a function to be launched to read the data:
NotificationMsg = 0x5555 ; An arbitrary message number, but should be greater than 0x1000.
OnMessage(NotificationMsg, "ReceiveData")
; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ = 1 ; Received when data is available to be read.
FD_CLOSE = 32 ; Received when connection has been closed.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
ExitApp
}
return
SendviaNet:
Gui, Submit, NoHide
SendText =%ClientName%: %SendText%
guicontrol,,SendText,
send1:
SendData(socket,SendText)
SendText =
return
ConnectToAddress(IPAddress, Port)
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative.
result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
; Since WSAStartup() will likely be the first Winsock function called by this script,
; check ErrorLevel to see if the OS has Winsock 2.0 available:
if ErrorLevel
{
MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
return -1
}
if result ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
{
MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_TCP = 6
socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
if socket = -1
{
MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}
; Prepare for connection:
SizeOfSocketAddress = 16
VarSetCapacity(SocketAddress, SizeOfSocketAddress)
InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family
InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port
InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr
; Attempt connection:
if DllCall("Ws2_32\connect", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
{
MsgBox % "connect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}
return socket ; Indicate success by returning a valid socket ID rather than -1.
}
ReceiveData(wParam, lParam)
; By means of OnMessage(), this function has been set up to be called automatically whenever new data
; arrives on the connection.
{
Critical
global ShowRecieved
socket := wParam
ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
Loop ; This loop solves the issue of the notification message being discarded due to thread-already-running.
{
VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; 0 for last param terminates string for use with recv().
ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
if ReceivedDataLength = 0 ; The connection was gracefully closed,
ExitApp ; The OnExit routine will call WSACleanup() for us.
if ReceivedDataLength = -1
{
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if WinsockError = 10035 ; WSAEWOULDBLOCK, which means "no more data to be read".
return 1
if WinsockError <> 10054 ; WSAECONNRESET, which happens when Network closes via system shutdown/logoff.
; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
MsgBox % "recv() indicated Winsock error " . WinsockError
ExitApp ; The OnExit routine will call WSACleanup() for us.
}
; Otherwise, process the data received.
Loop, parse, ReceivedData, `n, `r
{
ShowRecieved = %ShowRecieved%%A_LoopField%
fileappend, %ShowRecieved%`n, %A_Desktop%\client.txt
fileread, chat1, %A_Desktop%\client.txt
guicontrol,,chat, %chat1%
ShowRecieved =
ControlSend Edit1, ^{End}, Client
}
}
return 1 ; Tell the program that no further processing of this message is needed.
}
SendData(wParam,SendData)
{
socket := wParam
;MsgBox %socket% %SendData%
SendDataSize := VarSetCapacity(SendData)
SendDataSize += 1
sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendData, "Int", SendDatasize, "Int", 0)
;send( sockConnected,> welcome, strlen(welcome) + 1, NULL);
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}
ExitSub: ; This subroutine is called automatically when the script exits for any reason.
; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
; deallocated as if closesocket was called."
gui, destroy
DllCall("Ws2_32\WSACleanup")
;sleep, 2000
filedelete, %A_Desktop%\client.txt
ExitApp
Guiclose:
filedelete, %A_Desktop%\client.txt
gosub, exitsub
|
Server:
| Code: |
; -------------------------------------------------
; ------------SERVERSCRIPT------------------
; -------------------------------------------------
; CONFIGURATION SECTION:
; Specify Your own Network's address and port.
Network_Address = 192.168.1.101
Network_Port = 1000
; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------
Gui, add, edit, w180 h200 vchat
Gui, Add, Edit, w100 vSendText
Gui, Add, Button, gSendviaNet, Send
gui, add, button, , Hide
Gui, Show,, Server
Gosub Connection_Init
return
buttonhide:
winhide, Server
return
^!s::
winshow, Server
return
$Enter::
ifwinactive, Client
{
goto, SendviaNet
}
else
send, {ENTER}
return
Connection_Init:
OnExit, ExitSub ; For connection cleanup purposes.
; set up a very basic server:
socket := PrepareForIncomingConnection(Network_Address, Network_Port)
if socket = -1 ; Connection failed (it already displayed the reason).
ExitApp
; Find this script's main window:
Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off
; When the OS notifies the script that there is incoming data waiting to be received,
; the following causes a function to be launched to read the data:
NotificationMsg = 0x5555 ; An arbitrary message number, but should be greater than 0x1000.
OnMessage(NotificationMsg, "ReceiveData")
; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ = 1 ; Received when data is available to be read.
FD_CLOSE = 32 ; Received when connection has been closed.
FD_CONNECT = 20 ; Recieved when connection has been made.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CONNECT)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
ExitApp
}
i=1
Loop ; Wait for incomming connections
{
; accept requests that are in the pipeline of the socket
conectioncheck%i% := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
; Ws2_22/accept returns the new Connection-Socket if a connection request was in the pipeline
; on failure it returns an negative value
if conectioncheck%i% > 1
{
i++
s = socket
VarSetCapacity(sockaddr, 16, 0)
DllCall("Ws2_32\getpeername", "uint", s, "uint", &sockaddr, "int*", 16)
peername := DllCall("Ws2_32\inet_ntoa", "uint", NumGet(sockaddr,4), "str")
. ":" NumGet(sockaddr,2,"ushort")
msgbox %peername%
}
sleep 500 ; wait half 1 second then accept again
}
return
SendviaNet:
Gui, Submit, NoHide
SendText =Server-%SendText%
guicontrol,,SendText,
go:
fileappend, %SendText%`n, %A_Desktop%\server.txt
fileread, chat2, %A_Desktop%\server.txt
guicontrol,,chat, %chat2%
controlsend, chat, ^{end}, Server
gosub, send1
resend:
SendText= %ShowRecieved%
ShowRecieved=
send1:
Loop %i% {
SendData(conectioncheck%A_Index%,SendText)
}
SendText =
return
PrepareForIncomingConnection(IPAddress, Port)
; This can connect to most types of TCP servers, not just Network.
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative.
result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
; Since WSAStartup() will likely be the first Winsock function called by this script,
; check ErrorLevel to see if the OS has Winsock 2.0 available:
if ErrorLevel
{
MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
return -1
}
if result ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
{
MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_TCP = 6
socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
if socket = -1
{
MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}
; Prepare for connection:
SizeOfSocketAddress = 16
VarSetCapacity(SocketAddress, SizeOfSocketAddress)
InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family
InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port
InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr
; Bind to socket:
if DllCall("Ws2_32\bind", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
{
MsgBox % "bind() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}
if DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN")
{
MsgBox % "LISTEN() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}
return socket ; Indicate success by returning a valid socket ID rather than -1.
}
ReceiveData(wParam, lParam)
; By means of OnMessage(), this function has been set up to be called automatically whenever new data
; arrives on the connection.
{
Critical
global ShowRecieved
socket := wParam
ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
Loop ; This loop solves the issue of the notification message being discarded due to thread-already-running.
{
VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; 0 for last param terminates string for use with recv().
ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
if ReceivedDataLength = -1
{
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if WinsockError = 10035 ; WSAEWOULDBLOCK, which means "no more data to be read".
return 1
if WinsockError <> 10054 ; WSAECONNRESET, which happens when Network closes via system shutdown/logoff.
; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
MsgBox % "recv() indicated Winsock error " . WinsockError
ExitApp ; The OnExit routine will call WSACleanup() for us.
}
; Otherwise, process the data received.
Loop, parse, ReceivedData, `n, `r
{
ShowRecieved = %ShowRecieved%%A_LoopField%
fileappend, %ShowRecieved%`n, %A_Desktop%\server.txt
fileread, chat2, %A_Desktop%\server.txt
guicontrol,,chat, %chat2%
controlsend, chat, ^{end}, Server
gosub, resend
;ShowRecieved =
}
}
return 1 ; Tell the program that no further processing of this message is needed.
}
SendData(wParam,SendData)
{
socket := wParam
;MsgBox %socket% %SendData%
SendDataSize := VarSetCapacity(SendData)
SendDataSize += 1
sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendData, "Int", SendDatasize, "Int", 0)
;send( sockConnected,> welcome, strlen(welcome) + 1, NULL);
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}
ExitSub: ; This subroutine is called automatically when the script exits for any reason.
; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
; deallocated as if closesocket was called."
filedelete, %A_Desktop%\server.txt
DllCall("Ws2_32\WSACleanup")
ExitApp
guiclose:
filedelete, %A_Desktop%\server.txt
exitapp | [/code] |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Wed May 21, 2008 8:46 am Post subject: |
|
|
You have not changed 's' to 'socket'; you have assigned the literal string "socket" to the variable 's'. Even then, the socket you want is probably conectioncheck%i%, since the value returned by accept() is the socket on which the actual connection is made.
After changing it to either socket or conectioncheck%i%, it still doesn't work. Rather than using getpeername, I suggest you use accept() correctly...
| Code: | | conectioncheck%i% := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress) | There are two issues with the above:
- SocketAddress and SizeOfSocketAddress are never assigned values, since they are not declared as global in PrepareForIncomingConnection. Because the blank SizeOfSocketAddress is interpreted as zero, accept() simply ignores them.
- The addrlen parameter is a pointer to an int, not just an int.
Try the following:
| Code: | VarSetCapacity(SocketAddress, SizeOfSocketAddress:=16)
conectioncheck%i% := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int*", SizeOfSocketAddress)
if conectioncheck%i% != -1
{
i++
peername := DllCall("Ws2_32\inet_ntoa", "uint", NumGet(SocketAddress,4), "str")
. ":" NumGet(SocketAddress,2,"ushort")
MsgBox % peername
} | Also note that I check for success with conectioncheck%i% != -1 rather than conectioncheck%i% > 1. The only documented invalid socket value as far as I know is -1. 1 may well be a valid socket handle. |
|
| Back to top |
|
 |
Zippo() Guest
|
Posted: Wed May 21, 2008 8:55 am Post subject: |
|
|
| Lexikos wrote: | | Code: | | conectioncheck%i% := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress) | . |
Erm the Int SocketAddress is an in/out, you need to pass its address (after you set it).
| MSDN wrote: | | The parameter addr is a result parameter that is filled in with the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family in which the communication is occurring. The addrlen is a value-result parameter; it should initially contain the amount of space pointed to by addr; on return it will contain the actual length (in bytes) of the address returned. |
|
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Wed May 21, 2008 12:17 pm Post subject: |
|
|
It seems Zippo() skipped over half my post. The point of highlighting it in red was to show it is incorrect. |
|
| Back to top |
|
 |
U90 Guest
|
Posted: Wed May 21, 2008 3:56 pm Post subject: |
|
|
I like what jmanx did with this. It seems to work good in Vista too. But, how to make a Disconnect button on the client interface??  |
|
| Back to top |
|
 |
LYCOS17
Joined: 06 Feb 2008 Posts: 20 Location: Dallas
|
Posted: Wed May 21, 2008 4:51 pm Post subject: |
|
|
Hi all, I am just slightly confused on this whole contraption. Can I use it to connect to a buddy of mine in another location -- or does this need to be on the same internal local network? Like, when I test this on my machine, if I put my public IP address in the Network_Address = field, on the Server script, I get a Winsock Binding error... tho I know the address is true. What am I doing wrong here? I read in this thread that one has to put a 'local' IP there, like 127.0.0.1... so you mean I cannot put my public IP there?.... but can this client/server script have this purpose I have in mind, other than for locally connected internal computers? If my friend is basically not in the same room with me then this script is useless.... or am I completely missing some concept? What I mean is,
Can I run the Server Script here in Dallas
.....and you run the Client script in France, for instance.
Can we connect up?
Please help me out with the conceptual understanding or what I am failing to understand! Ty!! |
|
| Back to top |
|
 |
ahklerner
Joined: 26 Jun 2006 Posts: 1205 Location: USA
|
Posted: Wed May 21, 2008 4:58 pm Post subject: |
|
|
here is probably what you are faced with:
| Code: | | Friend's PC IP (192.168.x.x)-> Friend's Modem / router IP(x.x.x.x) -> [Internet] <- Your Modem / router IP (x.x.x.x) <- Your PC IP (192.168.x.x) |
For you to connect to your friend:
-the server address will be the IP of your friends modem.
-your friends modem / router must forward all traffic on whatever port you are using to his pc IP address. _________________
 |
|
| Back to top |
|
 |
jmanx
Joined: 14 May 2008 Posts: 40
|
Posted: Wed May 21, 2008 8:18 pm Post subject: |
|
|
It works now thanks.
Is there anyway to define when a certain address disconnects though? |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|