New FWH 25.01
Posted: Wed Feb 19, 2025 8:16 am
www.FiveTechSoft.com
https://forums.fivetechsupport.com/~fivetec1/forums/
https://forums.fivetechsupport.com/~fivetec1/forums/viewtopic.php?t=45433
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
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
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 )
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
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
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
Code: Select all | Expand
LOCAL oGmail
oGmail := Tgmail():New()
Code: Select all | Expand
oGmail:Init( "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", "YOUR_REDIRECT_URI" )
Code: Select all | Expand
LOCAL cAuthUrl
cAuthUrl := oGmail: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" // 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
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
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 )
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
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
Silvio.Falconi wrote: Wed Feb 26, 2025 9:06 am 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