 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
daonlyfreez
Joined: 16 Mar 2005 Posts: 745 Location: Berlin
|
Posted: Mon Jan 29, 2007 4:02 pm Post subject: Winsock 2 - SMTP/POP with AutoHotkey? |
|
|
Hi all,
I took the client/server TCP scripts from Zed Gecko, and tried to change it to be able to send/retreive e-mail.
I'm having difficulties with the commands. Some commands are not accepted, throwing errors.
Please experiment and tell me your findings...
You need to adapt the server:port variables in the script
| Code: |
/* Example calls...
SMTP (port 25):
HELO
MAIL FROM:<someone@someserver.com>
RCPT TO:<someone@someotherserver.com>
DATA
From: "Some One" <someone@someserver.com>
To: <someone@someotherserver.com>
Subject: Hi there, how are you?
"Message body"
.
QUIT
POP (port 110)
HELO
USER someone@someserver.com
PASS password
STAT
LIST
TOP 1 10
QUIT
IRC (port 6667)
/list /list -*ahk*
/join #channel
hi there
/quit
*/
Remote_Address_Org = localhost
; 127.0.0.1 ; 192.168.178.20 ; smtp.someserver.com ; pop.someserver.com
Remote_Port = 110 ; 25 ; 6667
; Get IP if domain
StringReplace, Remote_Address_Stripped, Remote_Address_Org, `., , A
If Remote_Address_Stripped is not number
{
IPs := HostToIp(Remote_Address_Org)
DllCall("Ws2_32\WSACleanup") ; always inlude this line after calling to release the socket connection
if IPs <> -1 ; no error occurred
{
If IPs contains `n
{
StringSplit, anIP, IPs, `n
/*
Gui, +AlwaysOnTop +Owner
Gui, 2: Add, Text, , Multiple IPs found! Select IP to use.
Gui, 2: Add, ListBox, vIPSelected, a||b|c
Gui, 2: Add, Button, x100 gIPCancel, Cancel
Gui, 2: Show, w300 h200, Multiple IPs!
Return
*/
; using first ip found
Remote_Address := anIP1
}
Else
Remote_Address := IPs
}
else
{
MsgBox, 16, Error!, Host "%Remote_Address_Org%" not found`, Exiting...
Gosub, ExitSub
}
}
Else
Remote_Address := Remote_Address_Org
Gui, Add, Button, x10 y10 w60 h20 gConnection_Init, Connect
Gui, Add, Text, x80 y14 w300 h20, with domain: %Remote_Address_Org%. IP = %remote_address%:%remote_port%
Gui, Add, Button, x450 y10 w60 h20 gClear, Clear
Gui, Add, Edit, x10 y40 w500 R10 vMyEdit
Gui, Add, Edit, x10 y190 w320 R1 vToSend
Gui, Add, Button, x340 y190 w160 h20 gSendCommand, Send Command to Server
Gui, Add, Button, x10 y220 w80 h20 gSMTPHelp, SMTP Help
Gui, Add, Button, x120 y220 w80 h20 gPOPHelp, POP Help
Gui, Add, Button, x450 y240 w60 h20 gExitSub, Exit
Gui, Show, , AutoHotkey - Winsock 2 - SMTP/POP Test
LinesReceived:=0
Return
; --------------------------
IPOk:
Return
IPCancel:
Return
SMTPHelp:
MsgBox, 64, SMTP Help - RFC 821,
(LTrim Join`n
HELO <SP> <domain> <CRLF>
MAIL <SP> FROM:<reverse-path> <CRLF>
RCPT <SP> TO:<forward-path> <CRLF>
DATA <CRLF>
RSET <CRLF>
SEND <SP> FROM:<reverse-path> <CRLF>
SOML <SP> FROM:<reverse-path> <CRLF>
SAML <SP> FROM:<reverse-path> <CRLF>
VRFY <SP> <string> <CRLF>
EXPN <SP> <string> <CRLF>
HELP [<SP> <string>] <CRLF>
NOOP <CRLF>
QUIT <CRLF>
TURN <CRLF>
)
Return
POPHelp:
MsgBox, 64, POP Help - RFC1225,
(LTrim Join`n
USER <SP> <string> <CRLF>
PASS <SP> <string> <CRLF>
STAT <CRLF>
LIST [<SP> <msgid>] <CRLF>
DELE <SP> <msgid> <CRLF>
TOP <SP> <msgid> <SP> <linecount> <CRLF>
RETR <SP> <msgid> <CRLF>
QUIT <CRLF>
NOOP <CRLF>
RPOP <SP> <string> <CRLF>
)
Return
Connection_Init:
OnExit, ExitSub ; For connection cleanup purposes.
; set up a very basic server:
;localsocket := PrepareForIncomingConnection(Network_Address, Network_Port)
;if localsocket = -1 ; Connection failed (it already displayed the reason).
; ExitApp
; Connect to any type of server:
remotesocket := ConnectToAddress(Remote_Address, Remote_Port)
if remotesocket = -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", remotesocket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
ExitApp
}
Loop ; Wait for incomming connections
{
; accept requests that are in the pipeline of the socket
conectioncheck := DllCall("Ws2_32\accept", "UInt", remotesocket, "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 > 1
{
MsgBox Incoming connection accepted
break
}
sleep 500 ;500 ; wait half 1 second then accept again
}
return
Clear:
ShowReceived=
LinesReceived:=0
GuiControl,, MyEdit,
return
SendCommand:
GuiControlGet, sendData, , ToSend
;msgbox % "|" senddata "|"
SendData(remotesocket,SendData . "`r`n")
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.
}
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.
}
SendData(wParam,SendData, Repeat=1, Delay=0)
{
socket := wParam
; SendDataSize := VarSetCapacity(SendData)
; SendDataSize += 1
Loop % Repeat
{
SendIt := SendData . "(" . A_Index . ")"
SendDataSize := VarSetCapacity(SendIt)
SendDataSize += 1
sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendIt, "Int", strlen(SendIt), "Int", 0)
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if WinsockError <> 0 ; 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 % "send() indicated Winsock error " . WinsockError
sleep,Delay
}
;send( sockConnected,> welcome, strlen(welcome) + 1, NULL);
}
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 ShowReceived
global MyEdit
global LinesReceived
socket := wParam
ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
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 ; Should probably never happen since we were notified there is data on the connection, yet now we're told there's none?
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.
}
; Since above didn't return or exit, process the data that was just received.
Loop ; For each binary-zero-delimited segment in the data.
{
Loop, parse, ReceivedData, `n, `r ; For each line in this segment.
{
LinesReceived++
if (LinesReceived = 1) {
ShowReceived = %LinesReceived%: %A_LoopField%
} else {
ShowReceived = %ShowReceived%`n%LinesReceived%: %A_LoopField%
}
;Tooltip % ShowReceived
GuiControl,, MyEdit, %ShowReceived%
}
ReceivedDataLengthApparent := strlen(ReceivedData)
if (ReceivedDataLength-1 <= ReceivedDataLengthApparent) ; -1 to adjust for the legitimate/last zero-termintor at the end of the last segment.
break ; No more binary-zero-delimited segements are present.
; Otherwise, there's a binary zero "hiding" more data that lies to its right.
DllCall("RtlMoveMemory", str, ReceivedData ; Shift the data leftward to eliminate from consideration the segement that was just processed.
, UInt, &ReceivedData + ReceivedDataLengthApparent + 1
, UInt, ReceivedDataLength - ReceivedDataLengthApparent)
ReceivedDataLength -= ReceivedDataLengthApparent + 1 ; Adjust length to reflect actual NEW length of ReceivedData.
}
return 1 ; Tell the program that no further processing of this message is needed.
}
HostToIp(NodeName) ; returns -1 if unsuccessfull or a newline seperated list of valid IP addresses on 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)
if ErrorLevel ; check ErrorLevel to see if the OS has Winsock 2.0 available:
{
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 on success).
{
MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") ; %
return -1
}
PtrHostent := DllCall("Ws2_32\gethostbyname", str, Nodename)
if (PtrHostent = 0)
Return -1
VarSetCapacity(hostent,16,0)
DllCall("RtlMoveMemory",UInt,&hostent,UInt,PtrHostent,UInt,16)
h_name := ExtractInteger(hostent,0,false,4)
h_aliases := ExtractInteger(hostent,4,false,4)
h_addrtype := ExtractInteger(hostent,8,false,2)
h_length := ExtractInteger(hostent,10,false,2)
h_addr_list := ExtractInteger(hostent,12,false,4)
; Retrieve official name
VarSetCapacity(Name,64,0)
DllCall("RtlMoveMemory",UInt,&Name,UInt,h_name,UInt,64)
; Retrieve Aliases
VarSetCapacity(Aliases,12,0)
DllCall("RtlMoveMemory", UInt, &Aliases, UInt, h_aliases, UInt, 12)
Loop, 3
{
offset := ((A_Index-1)*4)
PtrAlias%A_Index% := ExtractInteger(Aliases,offset,false,4)
If (PtrAlias%A_Index% = 0)
break
VarSetCapacity(Alias%A_Index%,64,0)
DllCall("RtlMoveMemory",UInt,&Alias%A_Index%,UInt,PtrAlias%A_Index%,Uint,64)
}
VarSetCapacity(AddressList,12,0)
DllCall("RtlMoveMemory",UInt,&AddressList,UInt,h_addr_list,UInt,12)
Loop, 3
{
offset := ((A_Index-1)*4)
PtrAddress%A_Index% := ExtractInteger(AddressList,offset,false,4)
If (PtrAddress%A_Index% =0)
break
VarSetCapacity(address%A_Index%,4,0)
DllCall("RtlMoveMemory" ,UInt,&address%A_Index%,UInt,PtrAddress%A_Index%,Uint,4)
i := A_Index
Loop, 4
{
if Straddress%i%
Straddress%i% := Straddress%i% "." ExtractInteger(address%i%,(A_Index-1 ),false,1)
else
Straddress%i% := ExtractInteger(address%i%,(A_Index-1 ),false,1)
}
Straddress0 = %i%
}
loop, %Straddress0% ; put them together and return them
{
_this := Straddress%A_Index%
if _this <>
IPs = %IPs%%_this%
if A_Index = %Straddress0%
break
IPs = %IPs%`n
}
return IPs
}
ExtractInteger(ByRef sr, o = 0, is = false, s = 4)
{
Loop %s%
r += *(&sr + o + A_Index-1) << 8*(A_Index-1)
If (!is OR s > 4 OR r < 0x80000000)
Return r
Return -(0xFFFFFFFF - r + 1)
}
InsertInteger(i, ByRef d, o = 0, s = 4)
{
Loop %s%
DllCall("RtlFillMemory", "UInt", &d + o + A_Index-1, "UInt", 1, "UChar", i >> 8*(A_Index-1) & 0xFF)
}
GuiEscape:
guiclose:
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."
DllCall("Ws2_32\WSACleanup")
ExitApp
|
Edit: Updated code _________________ (sorry, homesite offline atm)
Last edited by daonlyfreez on Mon Jan 29, 2007 10:18 pm; edited 1 time in total |
|
| Back to top |
|
 |
bnbn2000
Joined: 10 Jul 2006 Posts: 4
|
Posted: Mon Jan 29, 2007 8:15 pm Post subject: |
|
|
| give a try on CMD utility like blat/getmail ? |
|
| Back to top |
|
 |
daonlyfreez
Joined: 16 Mar 2005 Posts: 745 Location: Berlin
|
Posted: Mon Jan 29, 2007 9:57 pm Post subject: |
|
|
| bnbn2000 wrote: | | give a try on CMD utility like blat/getmail ? |
Hi bnbn2000,
Well, I know and use blat/getmail already. Would be nice to have an AHK only solution.
These first attempts at directly using Winsock 2 could work (if they work), with any TCP/IP protocol, for example POP/SMTP, IRC...
TCP/IP protocols
Btw... Script updated
Got POP/SMTP working on a local mailserver, trying IRC now... _________________ (sorry, homesite offline atm) |
|
| 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
|