New FWH 25.01
- Antonio Linares
- Site Admin
- Posts: 42573
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Has thanked: 36 times
- Been thanked: 84 times
- Contact:
- Antonio Linares
- Site Admin
- Posts: 42573
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Has thanked: 36 times
- Been thanked: 84 times
- Contact:
Re: New FWH 25.01
Hi
What about this :
We will provide support for the oAuth2 on the next version FWH
with samples to send e-mail using GMail/Outlook(Office365).
Thanks,
Philippe
What about this :
We will provide support for the oAuth2 on the next version FWH
with samples to send e-mail using GMail/Outlook(Office365).
Thanks,
Philippe
- Silvio.Falconi
- Posts: 7151
- Joined: Thu Oct 18, 2012 7:17 pm
- Been thanked: 1 time
Re: New FWH 25.01
I think it is an initiative by laiton maybe antonio is not awareJack wrote: Wed Feb 19, 2025 3:14 pm Hi
What about this :
We will provide support for the oAuth2 on the next version FWH
with samples to send e-mail using GMail/Outlook(Office365).
Thanks,
Philippe
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
- Antonio Linares
- Site Admin
- Posts: 42573
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Has thanked: 36 times
- Been thanked: 84 times
- Contact:
Re: New FWH 25.01
It is coming. We will publish an updated FWH 25.01 asap.Jack wrote: Wed Feb 19, 2025 3:14 pm Hi
What about this :
We will provide support for the oAuth2 on the next version FWH
with samples to send e-mail using GMail/Outlook(Office365).
Thanks,
Philippe
-
- Posts: 64
- Joined: Tue Feb 09, 2021 4:20 pm
Re: New FWH 25.01
My mix works with FWH 25.01
Many thanks.
Many thanks.
José M. C. Quintas Brazil
gtwvg, fivewin 25.01, hwgui, mingw 15.0 rc (32 bits)
gtwvg, fivewin 25.01, hwgui, mingw 15.0 rc (32 bits)
- Lailton
- Posts: 172
- Joined: Fri Jul 20, 2012 1:49 am
- Location: Brazil
- Has thanked: 2 times
- Been thanked: 7 times
- Contact:
Re: New FWH 25.01
Hello everyone,
An example of how Harbour + FWH works with OAuth.

Here is a sample of the code.
Code Sample
It will be included in FiveWin today for the next version. 
An example of how Harbour + FWH works with OAuth.

Here is a sample of the code.
Code Sample
Code: Select all | Expand
#include "fivewin.ch"
static oGmail, hStore
function main()
local oDlg
local oName, cName := ""
local oEmail, cEmail := ""
local oPhoto, oSend
local oConnect, oDisconnect
hStore := readStore( hb_dirBase() + "gmail.json" )
oGmail := TGmail():new()
oGmail:setConfig( {;
"client_id" => "your_client_id",;
"client_secret" => "your_client_secret",;
"redirect_uri" => "http://localhost:2025/";
} )
if !empty( hStore[ "token" ] )
oGmail:setToken( hStore[ "token" ] )
endif
define dialog oDlg resource "GMAIL"
redefine image oPhoto id 4002 of oDlg
redefine say oName var cName id 4003 of oDlg
redefine say oEmail var cEmail id 4004 of oDlg
redefine button oDisconnect id 4005 of oDlg action onDisconnect( oDlg, { oPhoto, oName, oEmail, oSend, oDisconnect }, { oConnect } )
redefine button oSend id 4006 of oDlg action onSendMail()
redefine button oConnect id 4001 of oDlg action onConnect( oDlg, { oPhoto, oName, oEmail, oSend, oDisconnect }, { oConnect } )
oDlg:bStart := { || updateControls( oDlg, { oPhoto, oName, oEmail, oSend, oDisconnect }, { oConnect } ) }
oDlg:lHelpIcon := .f.
activate dialog oDlg centered
saveStore( hb_dirBase() + "gmail.json", hStore )
return nil
function onConnect( oDlg, aConnect, aDisconnect )
local cToken
if !oGmail:isAuth()
cToken := oGmail:auth()
if !empty( cToken )
hStore[ "token" ] := cToken
else
msgStop( "Authentication failed!" )
endif
endif
updateControls( oDlg, aConnect, aDisconnect )
return nil
function onDisconnect( oDlg, aConnect, aDisconnect )
local cProfile := hb_dirBase() + "profile_gmail.jpg"
oGmail:revoke()
updateControls( oDlg, aConnect, aDisconnect )
if hb_vfExists( cProfile )
hb_vfErase( cProfile )
endif
return nil
function onSendMail()
if oGmail:send( "lailton@paysoft.com.br", "it is a test", "<b>Message from Gmail oAuth2</b>", .t., {} )
msgInfo( "Mail sent!" )
else
msgStop( "Failed to send email. You may not have authorized the required permissions..." )
endif
return nil
function updateControls( oDlg, aConnect, aDisconnect )
local hUser
local cProfile := hb_dirBase() + "profile_gmail.jpg"
if oGmail:isAuth()
hUser := oGmail:me()
endif
aEval( aConnect, { |o| o:hide() } )
aEval( aDisconnect, { |o| o:hide() } )
if hb_isHash( hUser )
aEval( aConnect, {|o|o:show(),o:refresh()} )
if !hb_vfExists( cProfile )
oGmail:downloadUrl( hUser[ "picture" ], cProfile )
endif
// Load Profile Photo
if hb_vfExists( cProfile )
aConnect[1]:loadImage(, cProfile )
aConnect[1]:refresh()
endif
aConnect[2]:setText( hUser[ "name" ] )
aConnect[3]:setText( hUser[ "email" ] )
aConnect[2]:update()
aConnect[3]:update()
else
aEval( aDisconnect, {|o|o:show(),o:refresh()} )
endif
oDlg:update()
return nil
function readStore( cFile )
local hStore
if hb_vfExists( cFile )
hStore := hb_jsonDecode( hb_memoRead( cFile ) )
endif
if !hb_isHash( hStore )
hStore := {;
"token" => "";
}
endif
return hStore
function saveStore( cFile, hStore )
hb_memoWrit( cFile, hb_jsonEncode( hStore ) )
return hb_vfExists( cFile )

Regards,
Lailton Fernando Mariano
Lailton Fernando Mariano
- Lailton
- Posts: 172
- Joined: Fri Jul 20, 2012 1:49 am
- Location: Brazil
- Has thanked: 2 times
- Been thanked: 7 times
- Contact:
Re: New FWH 25.01
Yes, It is TGmail. on the next days I will add too the version for Office365 following same idea.
Regards,
Lailton Fernando Mariano
Lailton Fernando Mariano
- Silvio.Falconi
- Posts: 7151
- Joined: Thu Oct 18, 2012 7:17 pm
- Been thanked: 1 time
Re: New FWH 25.01
I remember Cristobal made a Tgmail class some year ago
Have you tried this function ?
Make sure you have configured your application on Google Cloud Console and obtained the client_id, client_secret, and configured the redirect URL correctly.
Code: Select all | Expand
#include "FiveWin.ch"
FUNCTION SendEmail()
LOCAL cUrl, cData, cResponse
LOCAL cAccessToken := "YOUR_ACCESS_TOKEN" // Get token via OAuth2
LOCAL cMessage := '{"message": {"subject": "Test Email", "body": {"contentType": "Text", "content": "Hello, this is a test email!"}, "toRecipients": [{"emailAddress": {"address": "example@domain.com"}}]}}'
cUrl := "https://graph.microsoft.com/v1.0/me/sendMail"
cData := cMessage
cResponse := HttpPostRequest(cUrl, cData, "Authorization: Bearer " + cAccessToken)
IF !Empty(cResponse)
MsgInfo("Email sent successfully!", "Success")
ELSE
MsgError("Failed to send email.", "Error")
ENDIF
RETURN
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
- Silvio.Falconi
- Posts: 7151
- Joined: Thu Oct 18, 2012 7:17 pm
- Been thanked: 1 time
Re: New FWH 25.01
This is my Tgmail class Use oAuth2
SAMPLES
First of all, you need to create an object of the Tgmail class and initialize it with the authentication data.
// Initialize with the client_id, client_secret and redirect_uri obtained from Google Console
You can get the authorization URL to visit to get the authorization code.
Once the user has authorized the app and you have received the authorization code, you can get the access_token.
Once you have the access_token, you can send an email.
You will need some auxiliary functions like HttpPostRequest and Base64Encode. Here is an example of how you could implement them:
// Function to perform an HTTP POST request
//Function to encode in base64
This Tgmail class allows you to integrate with GMail via OAuth2, getting the access token and using it to send emails. Make sure you configure your application correctly on Google Cloud Console and manage the OAuth2 authorization flow.
Code: Select all | Expand
CLASS Tgmail
DATA cClientId // OAuth2 Client ID
DATA cClientSecret // OAuth2 Client Secret
DATA cRedirectUri // Redirect URI
DATA cAccessToken // OAuth2 Access Token
DATA cRefreshToken // OAuth2 Refresh Token
DATA cAuthUrl // Authorization URL
METHOD Init( cClientId, cClientSecret, cRedirectUri )
METHOD GetAuthorizationUrl()
METHOD GetAccessToken( cCode )
METHOD SendEmail( cSubject, cBody, cRecipient )
ENDCLASS
// Constructor for Tgmail class
METHOD Init( cClientId, cClientSecret, cRedirectUri )
::cClientId := cClientId
::cClientSecret := cClientSecret
::cRedirectUri := cRedirectUri
::cAuthUrl := "https://accounts.google.com/o/oauth2/v2/auth?scope=https://www.googleapis.com/auth/gmail.send&response_type=code&redirect_uri=" + SELF:cRedirectUri + "&client_id=" + SELF:cClientId
RETURN NIL
// Method to get the authorization URL
METHOD GetAuthorizationUrl()
RETURN ::cAuthUrl
// Method to get access token using authorization code
METHOD GetAccessToken( cCode )
LOCAL cUrl, cData, cResponse, cTokenUrl
LOCAL aJson
cTokenUrl := "https://oauth2.googleapis.com/token"
cData := "code=" + cCode + ;
"&client_id=" + ::cClientId + ;
"&client_secret=" + ::cClientSecret + ;
"&redirect_uri=" + ::cRedirectUri + ;
"&grant_type=authorization_code"
// Request to get the token
cResponse := HttpPostRequest( cTokenUrl, cData, "" )
IF !Empty(cResponse)
aJson := JsonParse( cResponse )
::cAccessToken := aJson[ "access_token" ]
::cRefreshToken := aJson[ "refresh_token" ]
RETURN .T.
ELSE
RETURN .F.
ENDIF
// Method to send an email via GMail
METHOD SendEmail( cSubject, cBody, cRecipient )
LOCAL cUrl, cMessage, cRaw, cResponse
// Prepare the message in MIME format
cRaw := "From: 'me'\r\n" + ;
"To: " + cRecipient + "\r\n" + ;
"Subject: " + cSubject + "\r\n" + ;
"Content-Type: text/plain; charset=UTF-8\r\n\r\n" + ;
cBody
// Encode the message in base64
cRaw := Base64Encode( cRaw )
// Build the request body
cMessage := '{"raw": "' + cRaw + '"}'
// URL to send the message
cUrl := "https://gmail.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=multipart"
// Send HTTP request with access token
cResponse := HttpPostRequest( cUrl, cMessage, "Authorization: Bearer " + ::cAccessToken )
IF !Empty(cResponse)
RETURN .T.
ELSE
RETURN .F.
ENDIF
RETURN NIL
SAMPLES
First of all, you need to create an object of the Tgmail class and initialize it with the authentication data.
Code: Select all | Expand
LOCAL oGmail
oGmail := Tgmail():New()
Code: Select all | Expand
oGmail:Init( "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", "YOUR_REDIRECT_URI" )
You can get the authorization URL to visit to get the authorization code.
Code: Select all | Expand
LOCAL cAuthUrl
cAuthUrl := oGmail:GetAuthorizationUrl()
MsgInfo( "Visita questo URL per autorizzare l'app: " + cAuthUrl, "Autorizzazione OAuth2" )
Once the user has authorized the app and you have received the authorization code, you can get the access_token.
Code: Select all | Expand
LOCAL cCode, lSuccess
cCode := "AUTHORIZATION_CODE_OBTAINED_FROM_USER" // Ottieni il codice di autorizzazione
lSuccess := oGmail:GetAccessToken( cCode )
IF lSuccess
MsgInfo( "Token di accesso ottenuto!", "Successo" )
ELSE
MsgError( "Errore durante l'ottenimento del token.", "Errore" )
ENDIF
Once you have the access_token, you can send an email.
Code: Select all | Expand
LOCAL lSuccess
lSuccess := oGmail:SendEmail( "Test Email", "Ciao, questa è una prova di invio email!", "recipient@example.com" )
IF lSuccess
MsgInfo( "Email inviata con successo!", "Successo" )
ELSE
MsgError( "Errore durante l'invio dell'email.", "Errore" )
ENDIF
// Function to perform an HTTP POST request
Code: Select all | Expand
FUNCTION HttpPostRequest( cUrl, cData, cHeaders )
LOCAL cResponse
cResponse := HttpPost( cUrl, cData, cHeaders )
RETURN cResponse
Code: Select all | Expand
FUNCTION Base64Encode( cData )
RETURN HB_BASE64ENCODE( cData )
This Tgmail class allows you to integrate with GMail via OAuth2, getting the access token and using it to send emails. Make sure you configure your application correctly on Google Cloud Console and manage the OAuth2 authorization flow.
Last edited by Silvio.Falconi on Wed Feb 26, 2025 1:42 pm, edited 1 time in total.
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
- Silvio.Falconi
- Posts: 7151
- Joined: Thu Oct 18, 2012 7:17 pm
- Been thanked: 1 time
Re: New FWH 25.01
My Office365 class
The Tgmail class I provided is specifically for integrating with the GMail API via OAuth2,
so it would not work directly for Office 365 (Outlook). However,
we can adapt it to also work with Office 365 (Microsoft Graph API),
which is the official API for interacting with Microsoft services, including Outlook and OneDrive.
Authentication via OAuth2:
To get the access_token, you will need to configure your application in the Azure Portal,
similar to how you did for GMail.
Microsoft Graph uses a similar authentication flow to GMail
but with different URLs and parameters.
Sending an email via Microsoft Graph:
The HTTP request to send the email will be directed to a different
URL than GMail, namely https://graph.microsoft.com/v1.0/me/sendMail.
Adapting the Tgmail class for Office 365 (Microsoft Graph API)
Below I show you how you can adapt the Tgmail class for Office 365.
The new class will be called Toffice365.
With this Toffice365 class, you can integrate your application with Office 365 (Microsoft Outlook) via OAuth2,
get an access_token,
and use it to send emails via the Microsoft Graph API.
SAMPLES as tgmail samples
Functions
// Funzione per eseguire una richiesta HTTP POST
FUNCTION HttpPostRequest( cUrl, cData, cHeaders )
LOCAL cResponse
cResponse := HttpPost( cUrl, cData, cHeaders )
RETURN cResponse
The Tgmail class I provided is specifically for integrating with the GMail API via OAuth2,
so it would not work directly for Office 365 (Outlook). However,
we can adapt it to also work with Office 365 (Microsoft Graph API),
which is the official API for interacting with Microsoft services, including Outlook and OneDrive.
Authentication via OAuth2:
To get the access_token, you will need to configure your application in the Azure Portal,
similar to how you did for GMail.
Microsoft Graph uses a similar authentication flow to GMail
but with different URLs and parameters.
Sending an email via Microsoft Graph:
The HTTP request to send the email will be directed to a different
URL than GMail, namely https://graph.microsoft.com/v1.0/me/sendMail.
Adapting the Tgmail class for Office 365 (Microsoft Graph API)
Below I show you how you can adapt the Tgmail class for Office 365.
The new class will be called Toffice365.
With this Toffice365 class, you can integrate your application with Office 365 (Microsoft Outlook) via OAuth2,
get an access_token,
and use it to send emails via the Microsoft Graph API.
Code: Select all | Expand
CLASS Toffice365
DATA cClientId // OAuth2 Client ID
DATA cClientSecret // OAuth2 Client Secret
DATA cRedirectUri // Redirect URI
DATA cAccessToken // OAuth2 Access Token
DATA cRefreshToken // OAuth2 Refresh Token
DATA cAuthUrl // Authorization URL
METHOD Init( cClientId, cClientSecret, cRedirectUri )
METHOD GetAuthorizationUrl()
METHOD GetAccessToken( cCode )
METHOD SendEmail( cSubject, cBody, cRecipient )
ENDCLASS
// Constructor for Toffice365 class
METHOD Init( cClientId, cClientSecret, cRedirectUri )
::cClientId := cClientId
::cClientSecret := cClientSecret
::cRedirectUri := cRedirectUri
::cAuthUrl := "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" + ;
"client_id=" + SELF:cClientId + ;
"&response_type=code" + ;
"&redirect_uri=" + SELF:cRedirectUri + ;
"&scope=Mail.Send"
RETURN NIL
// Method to get the authorization URL
METHOD GetAuthorizationUrl()
RETURN ::cAuthUrl
// Method to get access token using authorization code
METHOD GetAccessToken( cCode )
LOCAL cUrl, cData, cResponse, cTokenUrl
LOCAL aJson
cTokenUrl := "https://login.microsoftonline.com/common/oauth2/v2.0/token"
cData := "code=" + cCode + ;
"&client_id=" + SELF:cClientId + ;
"&client_secret=" + SELF:cClientSecret + ;
"&redirect_uri=" + SELF:cRedirectUri + ;
"&grant_type=authorization_code"
// Request to get the token
cResponse := HttpPostRequest( cTokenUrl, cData, "" )
IF !Empty(cResponse)
aJson := JsonParse( cResponse )
::cAccessToken := aJson[ "access_token" ]
::cRefreshToken := aJson[ "refresh_token" ]
RETURN .T.
ELSE
RETURN .F.
ENDIF
// Method to send an email via Office 365 (Microsoft Graph)
METHOD SendEmail( cSubject, cBody, cRecipient )
LOCAL cUrl, cMessage, cRaw, cResponse
// Prepara il messaggio in formato MIME
cRaw := '{"message": {' + ;
'"subject": "' + cSubject + '",' + ;
'"body": {"contentType": "Text", "content": "' + cBody + '"},' + ;
'"toRecipients": [{"emailAddress": {"address": "' + cRecipient + '"}}]' + ;
'}}'
// URL to send message via Microsoft Graph
cUrl := "https://graph.microsoft.com/v1.0/me/sendMail"
// Send HTTP request with access token
cResponse := HttpPostRequest( cUrl, cRaw, "Authorization: Bearer " + SELF:cAccessToken )
IF !Empty(cResponse)
RETURN .T.
ELSE
RETURN .F.
ENDIF
RETURN NIL
SAMPLES as tgmail samples
Code: Select all | Expand
LOCAL oOffice365
oOffice365 := Toffice365():New()
// Initializes with the client_id, client_secret and redirect_uri obtained from Azure Portal
oOffice365:Init( "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", "YOUR_REDIRECT_URI" )
Code: Select all | Expand
LOCAL cAuthUrl
cAuthUrl := oOffice365:GetAuthorizationUrl()
MsgInfo( "Visita questo URL per autorizzare l'app: " + cAuthUrl, "Autorizzazione OAuth2" )
Code: Select all | Expand
LOCAL cCode, lSuccess
cCode := "AUTHORIZATION_CODE_OBTAINED_FROM_USER" // Codice di autorizzazione
lSuccess := oOffice365:GetAccessToken( cCode )
IF lSuccess
MsgInfo( "Token di accesso ottenuto!", "Successo" )
ELSE
MsgError( "Errore durante l'ottenimento del token.", "Errore" )
ENDIF
Code: Select all | Expand
LOCAL lSuccess
lSuccess := oOffice365:SendEmail( "Test Email", "Ciao, questa è una prova di invio email!", "recipient@example.com" )
IF lSuccess
MsgInfo( "Email inviata con successo!", "Successo" )
ELSE
MsgError( "Errore durante l'invio dell'email.", "Errore" )
ENDIF
Functions
// Funzione per eseguire una richiesta HTTP POST
FUNCTION HttpPostRequest( cUrl, cData, cHeaders )
LOCAL cResponse
cResponse := HttpPost( cUrl, cData, cHeaders )
RETURN cResponse
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com