New FWH 25.01

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 42575
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 36 times
Been thanked: 84 times
Contact:

New FWH 25.01

Post by Antonio Linares »

regards, saludos

Antonio Linares
www.fivetechsoft.com
hua
Posts: 1077
Joined: Fri Oct 28, 2005 2:27 am
Has thanked: 1 time
Been thanked: 1 time

Re: New FWH 25.01

Post by hua »

What are the minimum Windows version required to run programs created with FWH+[x]Hb nowadays?
FWH 11.08/FWH 19.12
BCC5.82/BCC7.3
xHarbour/Harbour
User avatar
Antonio Linares
Site Admin
Posts: 42575
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 36 times
Been thanked: 84 times
Contact:

Re: New FWH 25.01

Post by Antonio Linares »

Dear Hua,

Not sure if we still support Windows 7
regards, saludos

Antonio Linares
www.fivetechsoft.com
Jack
Posts: 290
Joined: Wed Jul 11, 2007 11:06 am

Re: New FWH 25.01

Post by Jack »

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
User avatar
Silvio.Falconi
Posts: 7151
Joined: Thu Oct 18, 2012 7:17 pm
Been thanked: 1 time

Re: New FWH 25.01

Post by Silvio.Falconi »

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
I think it is an initiative by laiton maybe antonio is not aware
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
User avatar
Antonio Linares
Site Admin
Posts: 42575
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 36 times
Been thanked: 84 times
Contact:

Re: New FWH 25.01

Post by Antonio Linares »

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
It is coming. We will publish an updated FWH 25.01 asap.
regards, saludos

Antonio Linares
www.fivetechsoft.com
JoséQuintas
Posts: 64
Joined: Tue Feb 09, 2021 4:20 pm

Re: New FWH 25.01

Post by JoséQuintas »

My mix works with FWH 25.01

Many thanks.
José M. C. Quintas Brazil
gtwvg, fivewin 25.01, hwgui, mingw 15.0 rc (32 bits)
User avatar
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

Post by Lailton »

Hello everyone,

An example of how Harbour + FWH works with OAuth.
Image

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 )
It will be included in FiveWin today for the next version. :D
Regards,
Lailton Fernando Mariano
User avatar
vilian
Posts: 987
Joined: Wed Nov 09, 2005 2:17 am
Location: Brazil
Contact:

Re: New FWH 25.01

Post by vilian »

Hi Lailton

Is TGMAIL a new class ?
Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
User avatar
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

Post by Lailton »

vilian wrote: Tue Feb 25, 2025 6:51 pmTGmail
Yes, It is TGmail. on the next days I will add too the version for Office365 following same idea.
Regards,
Lailton Fernando Mariano
User avatar
Silvio.Falconi
Posts: 7151
Joined: Thu Oct 18, 2012 7:17 pm
Been thanked: 1 time

Re: New FWH 25.01

Post by Silvio.Falconi »

Lailton wrote: Tue Feb 25, 2025 6:53 pm
vilian wrote: Tue Feb 25, 2025 6:51 pmTGmail
Yes, It is TGmail. on the next days I will add too the version for Office365 following same idea.
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
User avatar
Silvio.Falconi
Posts: 7151
Joined: Thu Oct 18, 2012 7:17 pm
Been thanked: 1 time

Re: New FWH 25.01

Post by Silvio.Falconi »

This is my Tgmail class Use oAuth2

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()
// Initialize with the client_id, client_secret and redirect_uri obtained from Google Console

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
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

Code: Select all | Expand

FUNCTION HttpPostRequest( cUrl, cData, cHeaders )
   LOCAL cResponse
   cResponse := HttpPost( cUrl, cData, cHeaders )
   RETURN cResponse
//Function to encode in base64

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
User avatar
Silvio.Falconi
Posts: 7151
Joined: Thu Oct 18, 2012 7:17 pm
Been thanked: 1 time

Re: New FWH 25.01

Post by Silvio.Falconi »

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.

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
Post Reply