How to code in powershell a check if there are unread mails on an imap account? - powershell

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.

Related

EWS and AutoDiscoverURL error using Azure AD Certificate with Powershell

I've tried with and without Secret ID, and now with a self-signed Certificate and I keep getting the same error:
Exception calling "AutodiscoverUrl" with "2" argument(s): "The
expected XML node type was XmlDeclaration, but the actual type is
Element."
My PowerShell script:
$TenantId = "blahblah"
$AppClientId="blahblah"
$EDIcertThumbPrint = "blahblah"
$EDIcert = get-childitem Cert:\CurrentUser\My\$EDIcertThumbPrint
$MsalParams = #{
ClientId = $AppClientId
TenantId = $TenantId
ClientCertificate = $EDIcert
Scopes = "https://outlook.office.com/.default"
}
$MsalResponse = Get-MsalToken #MsalParams
$EWSAccessToken = $MsalResponse.AccessToken
Import-Module 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
#Provide the mailbox id (email address) to connect via AutoDiscover
$MailboxName ="email#myemaildomain.com.au"
$ews = [Microsoft.Exchange.WebServices.Data.ExchangeService]::new()
$ews.Credentials = [Microsoft.Exchange.WebServices.Data.OAuthCredentials]$EWSAccessToken
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
$ews.AutodiscoverUrl($MailboxName,{$true})
I've searched that error message everywhere, and I am not getting anywhere. The error doesn't make sense, because I am not referring to XML in any way - unless it's embedded inside the EWS?
The only time this works is when I don't use either a Secret ID nor a Certificate, but the Token only lasts 1 hour! I need to make this automatic, so I can get into my mailbox and extract files from emails.
Thanks
UPDATE
So I've removed the AutoDiscoverUrl() and I now getting another error:
Exception calling "FindItems" with "2" argument(s): "The request
failed. The remote server returned an error: (403) Forbidden."
Trace log:
The token contains not enough scope to make this call.";error_category="invalid_grant"
But why when I have an Oauth token!?
My code in trying to open the "Inbox":
$results = $ews.FindItems(
"Inbox",
( New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 100 )
)
$MailItems = $results.Items | where hasattachments
AutoDiscoverv1 doesn't support the client credentials flow so you need to remove the line
$ews.AutodiscoverUrl($MailboxName,{$true})
It's redundant anyway because your already setting the EWS endpoint eg
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
The only time that endpoint would change is if you had mailbox OnPrem in a hybrid environment and there are other ways you can go about detecting that such as autodiscoverv2.

Ktor raw sockets read channel read nothing right after connection established

I'm trying to connect to IMAP server with Ktor raw sockets
#KtorExperimentalAPI
override suspend fun start(host: SocketAddress) {
socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(host).tls(Dispatchers.IO)
writeChannel = socket.openWriteChannel()
readChannel = socket.openReadChannel()
val handshakeResponse = ByteArray(readChannel.availableForRead)
readChannel.readAvailable(handshakeResponse)
val handshake = String(handshakeResponse)
println(handshake)
}
After this connection I excpect that I'll get IMAP server response like this
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
And then I send IMAP commands to login to my mailbox
#KtorExperimentalAPI
override suspend fun command(command: String): String {
writeChannel.write("$command\r\n".also { print(it) })
val response = ByteArray(readChannel.availableForRead)
readChannel.readAvailable(response)
val responseString = String(response)
println(responseString)
return responseString
}
So my output should be like this
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
S1 LOGIN email pass
S1 OK Logged in
But for some reason, the output looks like these commands are executed in parallel threads
S1 LOGIN email pass
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
And my coroutine is blocked at write/read channels because Ktor use randezvous channels and I can't write to channel until I read output
My main code looks like this
fun run() {
val store = IMAPStore()
runBlocking(coroutineContext) {
//connect with tls (suspend function)
store.install()
//send login command (suspend function)
store.login()
}
}
I probably don’t quite understand how the connection through tls works and how the channels work. Maybe I'm starting coroutines wrong.
In this sample code the same logic works perfectly
fun main() {
runBlocking {
val socket =
aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(host)
.tls(Dispatchers.IO)
val w = socket.openWriteChannel(autoFlush = false)
val r = socket.openReadChannel()
r.readUTF8LineTo(System.out)
w.write("$TAG${tagCounter++} LOGIN email password\r\n")
r.readUTF8LineTo(System.out)
}
Can someone understand and explain this behavior?

Using CDO/SMTP/TLS in VB6 to send email smtp.office365.com mail server

I am searching for days to find out how can I set Office365 SMTP server in my VB6 application. My code is working properly with port 465 and other mail servers.
BUT it is not working with port 587 and smtp.office365.com
Is there any way I could have TLS via 587 in VB6?
Thanks
With this code, I get to send mail using CDO to Office365.
Private Message As CDO.Message
Private Attachment, Expression, Matches, FilenameMatch, i
Sub enviar_mail()
Set Message = New CDO.Message
Message.Subject = "Test Mail"
Message.From = "YourEmail#yourdomain.com"
Message.To = ""
Message.CC = ""
Message.TextBody = "my text body here"
Dim Configuration
Set Configuration = CreateObject("CDO.Configuration")
Configuration.Load -1 ' CDO Source Defaults
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.office365.com"
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "YourEmail#yourdomain.com"
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "YourPass"
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
Configuration.Fields.Update
Set Message.Configuration = Configuration
Message.Send
End Sub
This code worked for me until a few days ago when we switched ISP's (or maybe something changed coincidentally on the server side). If I specify port 587, I get a transport error and have not found the solution for that with VBA. Please let me know if this works for you and if you find a way to use 587. (Port 25 doesn't work for me either, same error.)
Public Function SMTPSend(vSendTo, vsubject As Variant, vmessage As Variant)
'This works
Set emailObj = CreateObject("CDO.Message")
emailObj.From = "name#myemail.com"
emailObj.To = vSendTo
emailObj.Subject = vsubject
emailObj.TextBody = vmessage
'emailObj.AddAttachment "c:\windows\win.ini"
Set emailConfig = emailObj.configuration
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.office365.com"
'Must exclude port if specifying SSL
'emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 587
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/sendusername") = "name#myemail.com"
emailConfig.Fields("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "mypassword"
emailConfig.Fields.Update
emailObj.Send
If Err.Number = 0 Then SMTPSend = True Else SMTPSend = False
End Function
There seem to be a number of posts on various forums suggesting that the CDO library doesn't work with port 587, but that's not true. I think the real issue is that SMTP services using port 587 are doing so because they require STARTTLS authentication and the http://schemas.microsoft.com/cdo/configuration/smtpusessl config option is specific to SSL, not TLS (I know, I know - not the most accurate description but it'll suffice for now).
Instead, there is another setting - http://schemas.microsoft.com/cdo/configuration/sendtls - that does support TLS, so using this with port 587 works fine:
config.Fields("http://schemas.microsoft.com/cdo/configuration/sendusing").Value = 2 '2 = cdoSendUsingPort
config.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver").Value = "smtp.example.com"
config.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport").Value = 587
config.Fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate").Value = 1 '1 = cdoBasic
config.Fields("http://schemas.microsoft.com/cdo/configuration/sendusername").Value = "my_username"
config.Fields("http://schemas.microsoft.com/cdo/configuration/sendpassword").Value = "myy_very_secret_password"
config.Fields("http://schemas.microsoft.com/cdo/configuration/sendtls").Value = True
config.Fields("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout").Value = 60
config.Fields.Update()
This is what worked for me:
flds("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
flds("http://schemas.microsoft.com/cdo/configuration/smtpserver")= "smtp.office365.com"
flds("http://schemas.microsoft.com/cdo/configuration/smtpserverport")= 25
flds("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate")= 1
flds("http://schemas.microsoft.com/cdo/configuration/sendusername")= "name#myemail.com"
flds("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "mypasword"
flds("http://schemas.microsoft.com/cdo/configuration/smtpusessl")= True
flds("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout")= 60
I don't know if you could fix it, but I got it with this:
Private Sub Command1_Click()
Dim oSmtp As New EASendMailObjLib.Mail
oSmtp.LicenseCode = "TryIt"
' Set your Hotmail email address
oSmtp.FromAddr = "liveid#hotmail.com"
' Add recipient email address
oSmtp.AddRecipientEx "support#emailarchitect.net", 0
' Set email subject
oSmtp.Subject = "test email from hotmail account"
' Set email body
oSmtp.BodyText = "this is a test email sent from VB 6.0 project with hotmail"
' Hotmail SMTP server address
oSmtp.ServerAddr = "smtp.live.com"
' Hotmail user authentication should use your
' Hotmail email address as the user name.
oSmtp.UserName = "liveid#hotmail.com"
oSmtp.Password = "yourpassword"
' Set port to 25, if you want to use 587 port, please change 25 to 587
oSmtp.ServerPort = 25
' detect SSL/TLS connection automatically
oSmtp.SSL_init
MsgBox "start to send email ..."
If oSmtp.SendMail() = 0 Then
MsgBox "email was sent successfully!"
Else
MsgBox "failed to send email with the following error:" & oSmtp.GetLastErrDescription()
End If
End Sub
Font: https://www.emailarchitect.net/easendmail/kb/vb.aspx?cat=4
Remember download the library:
http://easendmail-smtp-component-net-edition.soft112.com/
Just Replace Parameters!

Private only dovecot, local docker configuration for one user fails login for Apple Mail

I'm trying to make a local docker-dovecot machine to archive my e-mails. I would like to query them with Apple Mail. I have a simple ubuntu docker machine (on an VM with parallels, because I'm on a Mac).
I have this local.conf:
# A comma separated list of IPs or hosts where to listen in for connections.
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
# If you want to specify non-default ports or anything more complex,
# edit conf.d/master.conf.
listen = *,::
# Protocols we want to be serving.
protocols = imap
# Static passdb.
# This can be used for situations where Dovecot doesn't need to verify the
# username or the password, or if there is a single password for all users:
passdb {
driver = static
args = password=dovecot
}
# Location for users' mailboxes. The default is empty, which means that Dovecot
# tries to find the mailboxes automatically. This won't work if the user
# doesn't yet have any mail, so you should explicitly tell Dovecot the full
# location.
#
# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)
# isn't enough. You'll also need to tell Dovecot where the other mailboxes are
# kept. This is called the "root mail directory", and it must be the first
# path given in the mail_location setting.
#
# There are a few special variables you can use, eg.:
#
# %u - username
# %n - user part in user#domain, same as %u if there's no domain
# %d - domain part in user#domain, empty if there's no domain
# %h - home directory
#
# See doc/wiki/Variables.txt for full list. Some examples:
#
# mail_location = maildir:~/Maildir
# mail_location = mbox:~/mail:INBOX=/var/mail/%u
# mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n
#
# <doc/wiki/MailLocation.txt>
#
mail_location = maildir:/var/mail/%n
# System user and group used to access mails. If you use multiple, userdb
# can override these by returning uid or gid fields. You can use either numbers
# or names. <doc/wiki/UserIds.txt>
# mail_uid = CHANGE_THIS_to_your_short_user_name_or_uid
# mail_gid = admin
# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
ssl = no
# Login user is internally used by login processes. This is the most untrusted
# user in Dovecot system. It shouldn't have access to anything at all.
# default_login_user = _dovenull
# Internal user is used by unprivileged processes. It should be separate from
# login user, so that login processes can't disturb other processes.
# default_internal_user = _dovecot
# Setting limits.
default_process_limit = 10
default_client_limit = 50
and I'm getting this from Apple Mail
May 23 07:15:58 Mail[87524] <Debug>: <0x7fe16f021cd0:[Non-authenticated]> Wrote: 1.11 ID ("name" "Mac OS X Mail" "version" "9.3 (3124)" "os" "Mac OS X" "os-version" "10.11.5 (15F34)" "vendor" "Apple Inc.")
May 23 07:15:58 Mail[87524] <Debug>: <0x7fe16f021cd0:[Non-authenticated]> Read: * ID {
name = Dovecot;
}
May 23 07:15:58 Mail[87524] <Debug>: <0x7fe16f021cd0:[Non-authenticated]> Read: 1.11 OK
May 23 07:15:58 Mail[87524] <Debug>: <0x7fe16f021cd0:[Non-authenticated]> Wrote: 3.11 LOGOUT
May 23 07:16:00 Mail[87524] <Debug>: <0x7fe16aa14590:[Disconnected]> Read: * OK [CAPABILITY (
IMAP4REV1,
"LITERAL+",
"SASL-IR",
"LOGIN-REFERRALS",
ID,
ENABLE,
IDLE,
"AUTH=PLAIN",
"AUTH=LOGIN"
)]
May 23 07:16:00 Mail[87524] <Debug>: <0x7fe16aa14590:[Non-authenticated]> Wrote: 1.23 ID ("name" "Mac OS X Mail" "version" "9.3 (3124)" "os" "Mac OS X" "os-version" "10.11.5 (15F34)" "vendor" "Apple Inc.")
May 23 07:16:00 Mail[87524] <Debug>: <0x7fe16aa14590:[Non-authenticated]> Read: * ID {
name = Dovecot;
}
May 23 07:16:00 Mail[87524] <Debug>: <0x7fe16aa14590:[Non-authenticated]> Read: 1.23 OK
May 23 07:16:00 Mail[87524] <Debug>: <0x7fe16aa14590:[Non-authenticated]> Wrote: 3.23 LOGOUT
and this from dovecot (mail.log):
May 23 05:07:22 f8ab3e20742f dovecot: master: Dovecot v2.2.9 starting up (core dumps disabled)
May 23 05:07:22 f8ab3e20742f dovecot: ssl-params: Generating SSL parameters
May 23 05:07:29 f8ab3e20742f dovecot: ssl-params: SSL parameters regeneration completed
May 23 05:07:52 f8ab3e20742f dovecot: imap-login: Aborted login (no auth attempts in 0 secs): user=<>, rip=10.211.55.2, lip=172.17.0.2, session=<IJwtbHszbgAK0zcC>
May 23 05:07:54 f8ab3e20742f dovecot: imap-login: Aborted login (no auth attempts in 0 secs): user=<>, rip=10.211.55.2, lip=172.17.0.2, session=<qsRNbHszdgAK0zcC>
The output of doveconf -n is (so "disable_plaintext_auth = no" is active):
# 2.2.9: /etc/dovecot/dovecot.conf
# OS: Linux 4.4.8-boot2docker x86_64 Ubuntu 14.04.4 LTS aufs
auth_mechanisms = plain login
default_client_limit = 50
default_process_limit = 10
disable_plaintext_auth = no
listen = *,::
mail_location = maildir:/var/mail/%n
namespace inbox {
inbox = yes
location =
mailbox Drafts {
special_use = \Drafts
}
mailbox Junk {
special_use = \Junk
}
mailbox Sent {
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox Trash {
special_use = \Trash
}
prefix =
}
passdb {
args = password=dovecot
driver = static
}
protocols = imap
ssl = no
Any suggestions why this login isn't working?
Thanks!
The solution is to fix and configure the following line correctly (from local.conf):
# mail_uid = CHANGE_THIS_to_your_short_user_name_or_uid
How did I find out? Thanks to #Kondybas for the pointer to try another client. I used Thunderbird and it produced dovecot log entries (why didn't Apple Mail produce these lines? No clue), saying that it couldn't switch to mail_uid user context. I extended dovecot Dockerfile and switched the user appropriately. Afterwards it worked with Thunderbird and then with Apple Mail.

VBScript that sends an email without an SMTP server?

I'm trying to create a VBS script that sends an alert email when a folder has reached a specific file size, but I can't seem to get it to send an email. I get this error - "The transport failed to connect to the server". Is there any way to send an email without a SMTP server or?
I changed my pswrd/email an stuff for obv reasons.
Const dirPath = "C:\Users\tim.mcgee\Desktop\Offsite Drive"
Const alertedPath = "prevRun.txt"
alertOn = 3 * 2 ^ 29 '1.5GB
resetOn = alertOn * .95 'Approx 77MB
Const emailTo = "**"
Const emailFrom = "**"
Const emailSbjct = "Offsite Drive Full"
Const emailMsg = "The offsite drive has reached maximum capacity."
Const SMTPServer = "Smtp.gmail.com"
Const SMTPPort = 25
emailUsr = emailFrom
Const emailPsswd = "**"
Const emailSSL = False
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(alertedPath) Then
alerted = CBool(Trim(fso.OpenTextFile(alertedPath).ReadLine))
Else
alerted = False
End If
dirSize = fso.GetFolder(dirPath).Size
If alerted Then 'Email previously sent
alerted = dirSize > resetOn
ElseIf dirSize >= alertOn Then
SendEmail
alerted = True
End If
fso.OpenTextFile(alertedPath, 2, True).WriteLine CInt(alerted)
WScript.Quit 0
Sub SendEmail
Const cfg = "http://schemas.microsoft.com/cdo/configuration/"
With CreateObject("CDO.Message")
.From = emailFrom
.To = emailTo
.Subject = emailSbjct
.TextBody = emailMsg
With .Configuration.Fields
.Item(cfg & "sendusing") = 2
.Item(cfg & "smtpserver") = SMTPServer
.Item(cfg & "smtpserverport") = SMTPPort
.Item(cfg & "smtpconnectiontimeout") = 60
.Item(cfg & "smtpauthenticate") = 1
.Item(cfg & "smtpusessl") = emailSSL
.Item(cfg & "sendusername") = emailUsr
.Item(cfg & "sendpassword") = emailPsswd
.Update
End With
.Send
End With
End Sub
The error message means that your script can't connect to smtp.gmail.com on port 25. Nowadays most ISPs don't allow outbound mail on port 25 as a spam prevention measure. You need to send either through one of their mail relays, or the remote server must accept mail on a different port, usually 587 (submission) or 465 (SMTPS, deprecated).
Since you already have credentials you should probably just change the value of SMTPPort to 587 or 465. Gmail should accept authenticated mail on either of those ports.
As for your question about sending mail without an SMTP server, when using CDO you basically have 3 options for sending messages. You select the one you want to use via the sendusing configuration field:
cdoSendUsingPickup (numeric value 1): allows you to send mail without having to specify an SMTP server, but you must have an SMTP server installed on the host where the script is running. With this method the mail is submitted to the local SMTP server via a pickup folder. Does not require authentication, but the SMTP server must be configured to route mail correctly.
Normally, when you have a setup with local SMTP servers, these are configured to send all picked up mail to a central mail gateway/hub, which handles further delivery.
cdoSendUsingPort (numeric value 2, default): allows you to send mail to any SMTP server via the SMTP protocol. Also allows you to provide explicit credentials. With this method you must specify an SMTP server to send the mail to.
cdoSendUsingExchange (numeric value 3): allows you to send mail through the Exchange server of your domain. Requires a domain and an Exchange server, obviously.