It seem that I worded my question unclear here the reworded version:
How to code in powershell a script which checks on an third party imap account if there are unread mails. The account in mind uses TSL with user/pwd authorization.
IMAP is not a complex protocol, it's line-based and the number of relevant commands is limited, especially if you want nothing more than to check for unread mails in the inbox.
So it's pretty straightforward to build an IMAP client on top of System.Net.Sockets.TcpClient
. SSL/TLS is bit of a complication, but not too bad.
The conversation with an IMAP server goes like this:
Client: A001 command argument argument Server: * response line 1 * response line 2 A001 OK response line 3
Where A001
is the command tag, which is supposed to identify commands. Often it's in the form of a incrementing counter (a1
, a2
, a3
, ...) but really it can be anything. The server repeats the command tag in the final line of its response.
A sample conversation with a GMail IMAP server (authentication failed, obviously):
* OK Gimap ready for requests from 213.61.242.253 g189mb36880374lfe
a1 CAPABILITY
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH2 AUTH=PLAIN AUTH=PLAIN-CLIENTTOKEN AUTH=OAUTHBEARER AUTH=XOAUTH
a1 OK Thats all she wrote! g189mb36880374lfe
a2 LOGIN test test
a2 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)
a3 LOGOUT
* BYE Logout Requested g189mb36880374lfe
a3 OK Quoth the raven, nevermore... g189mb36880374lfe
The Powershell code that did this is not too complex:
using namespace System.IO;
using namespace System.Text;
using namespace System.Net.Sockets;
using namespace System.Net.Security;
using namespace System.Security.Cryptography.X509Certificates;
Set-StrictMode -Version 2.0
$DebugPreference = "Continue" # set to "SilentlyContinue" to hide Write-Debug output
$CRLF = "`r`n"
$server = "imap.gmail.com"
$port = 993
$username = "test"
$password = "test"
# connect to server
$client = [TcpClient]::new($server, $port)
$client.ReceiveTimeout = 2000 #milliseconds
# set up SSL stream, be lenient about the server's certificate
$acceptAnyCertificate = [RemoteCertificateValidationCallback] { $true }
$sslStream = [SslStream]::new($client.GetStream(), $false, $acceptAnyCertificate)
$sslStream.AuthenticateAsClient($server)
function StreamWrite {
param([Stream]$stream, [string]$command, [Encoding]$enc = [Encoding]::ASCII)
$data = $enc.GetBytes($command)
Write-Debug "> $($command.trim())"
$stream.Write($data, 0, $data.Length)
}
function StreamRead {
param([Stream]$stream, [int]$bufsize = 4*1KB, [Encoding]$enc = [Encoding]::ASCII)
$buffer = [byte[]]::new($bufsize)
$bytecount = $stream.Read($buffer, 0, $bufsize)
$response = $enc.GetString($buffer, 0, $bytecount)
Write-Debug "< $($response.trim())"
$response
}
# read server hello
$response = StreamRead $sslStream
StreamWrite $sslStream ("a1 CAPABILITY" + $CRLF)
$response = StreamRead $sslStream
# log in
StreamWrite $sslStream ("a2 LOGIN $username $password" + $CRLF)
$response = StreamRead $sslStream
# send mailbox commands...
# log out
StreamWrite $sslStream ("a3 LOGOUT" + $CRLF)
$response = StreamRead $sslStream
$sslStream.Close()
$client.Close()
Your mailbox command would probably be a simple select inbox
, to which the server responds with a bunch of info, including the number of unseen emails (an example can be seen on the Wikipedia):
C: a002 select inbox S: * 18 EXISTS S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) S: * 2 RECENT S: * OK [UNSEEN 17] Message 17 is the first unseen message S: * OK [UIDVALIDITY 3857529045] UIDs valid S: a002 OK [READ-WRITE] SELECT completed
You'll probably need to experiment a little with your mail server, but it should be easy to figure out the necessary details.
Read Connecting to smtp.live.com with the TcpClient class to get an idea how to do STARTTLS
instead of SSL
, if that's what your server requires.