﻿B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=5.51
@EndOfDesignText@
'version: 2.10
#Event: AccessTokenAvailable (Success As Boolean, Token As String)
Sub Class_Globals
	#if B4A
	Private LastIntent As Intent
	#end if
	Private evModule As Object
	Private evName As String
	Private mClientId As String
	Private mScope As String
	Type DpxTokenInformation (AccessToken As String, RefreshToken As String, AccessExpiry As Long, Valid As Boolean,Accountid As String)
	Private ti As DpxTokenInformation
	Private Const TokenFile As String = "oauth2token.dat"
	Private TokenFolder As String
	Private packageName As String 'ignore
	Private mClientSecret As String
#if B4J
	Private server As ServerSocket
	Private fx As JFX
	Private port As Int = 51067
	Private astream As AsyncStreams
#End if

	Private mApiV2urlContent As String = "https://content.dropboxapi.com/2/"
	Private mApiV2url As String = "https://api.dropboxapi.com/2/"
	
'	Dim scope As String = "account_info.read account_info.write contacts.write file_requests.read file_requests.write files.content.read files.content.write files.metadata.read files.metadata.write sharing.read sharing.write"
	
	
End Sub

#If B4J
Public Sub Initialize (Target As Object, EventName As String, ClientId As String, scope As String, ClientSecret As String, DataFolder As String)
#Else
Public Sub Initialize (Target As Object, EventName As String, ClientId As String, Scope As String)
#End If
	evModule = Target
	evName = EventName
	mClientId = ClientId
	mScope = scope
	#if B4A
		packageName = Application.PackageName
		TokenFolder = File.DirInternal
	#Else If B4i
		TokenFolder = File.DirLibrary
		packageName = GetPackageName
	#Else If B4J
		TokenFolder = DataFolder
		mClientSecret = ClientSecret
	#End If
	If File.Exists(TokenFolder, TokenFile) Then
		Dim raf As RandomAccessFile
		raf.Initialize(TokenFolder, TokenFile, True)
		ti = raf.ReadB4XObject(raf.CurrentPosition)
		raf.Close
	End If
End Sub

Private Sub SaveToken
	Dim raf As RandomAccessFile
	raf.Initialize(TokenFolder, TokenFile, False)
	raf.WriteB4XObject(ti, raf.CurrentPosition)
	raf.Close
End Sub

Public Sub ResetToken
	Log("Token reset!!!")
	ti.Valid = False
	SaveToken
End Sub

Public Sub GetAccessToken
   
   	If ti.Valid = False Then
		Authenticate
	Else If ti.AccessExpiry < DateTime.Now Then
		GetTokenFromRefresh
	Else
		RaiseEvent(True)
	End If
End Sub

Private Sub GetRedirectUri As String
	#if B4J
	Return "http://127.0.0.1:"&port
	#Else
		Return packageName & ":/oath"
	#End If
End Sub

Private Sub Authenticate
#if B4J
	PrepareServer
#End If
	Dim link As String = BuildLink("https://www.dropbox.com/oauth2/authorize", _
		 CreateMap("client_id": mClientId, _
		"redirect_uri": GetRedirectUri, _
		"response_type": "code", "scope": mScope ,"token_access_type":"offline"))
		
#if B4A
	Dim pi As PhoneIntents
	StartActivity(pi.OpenBrowser(link))
#else if B4i
	Main.App.OpenURL(link)
#else if B4J
	fx.ShowExternalDocument(link)
#end if
End Sub

#if B4J
Private Sub PrepareServer
	If server.IsInitialized Then server.Close
	If astream.IsInitialized Then astream.Close
	Do While True
		Try
			server.Initialize(port, "server")
			server.Listen
			Exit
		Catch
			port = port + 1
			Log(LastException)
		End Try
	Loop
	Wait For server_NewConnection (Successful As Boolean, NewSocket As Socket)
	If Successful Then
		astream.Initialize(NewSocket.InputStream, NewSocket.OutputStream, "astream")
		Dim Response As StringBuilder
		Response.Initialize
		Do While Response.ToString.Contains("Host:") = False
			Wait For AStream_NewData (Buffer() As Byte)
			Response.Append(BytesToString(Buffer, 0, Buffer.Length, "UTF8"))
		Loop
		astream.Write(("HTTP/1.0 200" & Chr(13) & Chr(10)).GetBytes("UTF8"))
		Sleep(50)
		astream.Close
		server.Close
		ParseBrowserUrl(Regex.Split2("$",Regex.MULTILINE, Response.ToString)(0))
	End If
	
End Sub
#else if B4A
Public Sub CallFromResume(Intent As Intent)
	If IsNewOAuth2Intent(Intent) Then
		LastIntent = Intent
		ParseBrowserUrl(Intent.GetData)
	End If
End Sub

Private Sub IsNewOAuth2Intent(Intent As Intent) As Boolean
	Return Intent.IsInitialized And Intent <> LastIntent And Intent.Action = Intent.ACTION_VIEW And _
		Intent.GetData <> Null And Intent.GetData.StartsWith(Application.PackageName)
End Sub
#else if B4I
Public Sub CallFromOpenUrl (url As String)
	If url.StartsWith(packageName & ":/oath") Then
		ParseBrowserUrl(url)
	End If
End Sub

Private Sub GetPackageName As String
	Dim no As NativeObject
	no = no.Initialize("NSBundle").RunMethod("mainBundle", Null)
	Dim name As Object = no.RunMethod("objectForInfoDictionaryKey:", Array("CFBundleIdentifier"))
	Return name
End Sub

#end if

Private Sub ParseBrowserUrl(Response As String)
	Dim m As Matcher = Regex.Matcher("code=([^&\s]+)", Response)
	If m.Find Then
		Dim code As String = m.Group(1) 'token
		GetTokenFromAuthorizationCode(code)
	Else
		Log("Error parsing server response: " & Response)
		ResetToken
		RaiseEvent(False)
	End If
End Sub

Private Sub RaiseEvent(Success As Boolean)
	CallSubDelayed3(evModule, evName & "_AccessTokenAvailable", Success, ti.AccessToken)
End Sub


Private Sub GetTokenFromAuthorizationCode (Code As String)
	Log("Getting access token from authorization code...")

	Dim j As HttpJob
	j.Initialize("", Me)
	Dim postString As String = $"code=${Code}&client_id=${mClientId}&grant_type=authorization_code&redirect_uri=${GetRedirectUri}"$
	postString = AddClientSecret(postString)
	j.PostString("https://www.dropbox.com/oauth2/token", postString)
		
	Wait For (j) JobDone(j As HttpJob)
	If j.Success Then
		TokenInformationFromResponse(j.GetString)
	Else
		ResetToken
		RaiseEvent(False)
	End If
	j.Release
End Sub

Private Sub GetTokenFromRefresh
	
	Log("Getting access token from refresh token...")
	Dim j As HttpJob
	j.Initialize("", Me)
	Dim postString As String = $"refresh_token=${ti.RefreshToken}&client_id=${mClientId}&grant_type=refresh_token"$
	postString = AddClientSecret(postString)
	j.PostString("https://api.dropbox.com/oauth2/token", postString)
	Wait For (j) JobDone(j As HttpJob)
	If j.Success Then
		TokenInformationFromResponse(j.GetString)
	Else
		RaiseEvent(False)
	End If
	j.Release
	
End Sub

Private Sub AddClientSecret (s As String) As String
	If mClientSecret <> "" Then 
		s = s & "&client_secret=" & mClientSecret
	
	End If
	Return s
End Sub

Private Sub TokenInformationFromResponse (s As String)
	
	Log("Server Response = "&s)
	
	
	Dim jp As JSONParser
	jp.Initialize(s)
	Dim m As Map = jp.NextObject
	ti.AccessExpiry = DateTime.Now + m.Get("expires_in") * 1000 - 5 * 60 * 1000
	ti.AccessToken = m.Get("access_token")
	If m.ContainsKey("refresh_token") Then ti.RefreshToken = m.Get("refresh_token")
	ti.Valid = True
	ti.Accountid = m.Get("account_id")
	
	Log($"Token received. Expires: $DateTime{ti.AccessExpiry}"$)
'	Log($"Dropbox Expires Time : $DateTime{ DateTime.Now + m.Get("expires_in")*1000}"$)
	
	SaveToken
	RaiseEvent(True)
End Sub



Private Sub BuildLink(Url As String, Params As Map) As String
	Dim su As StringUtils
	Dim sb As StringBuilder
	sb.Initialize
	sb.Append(Url)
	If Params.Size > 0 Then
		sb.Append("?")
		For Each k As String In Params.Keys
			sb.Append(su.EncodeUrl(k, "utf8")).Append("=").Append(su.EncodeUrl(Params.Get(k), "utf8"))
			sb.Append("&")
		Next
		sb.Remove(sb.Length - 1, sb.Length)
	End If
	Return sb.ToString
End Sub


' Searches for files and folders. 
Sub SearchFileId(filename As String) As ResumableSub
	
	Dim root As Map
	Dim path As String= "" 'root folder
	Dim job As HttpJob
	job.Initialize("filesearch",Me)
	job.PostString(mApiV2url&"files/search",$"{"path":"${path}", "query": "${filename}"}"$)
	job.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	job.GetRequest.SetHeader("Content-Type", "application/json")
	job.GetRequest.SetContentType("application/json")
	job.GetRequest.SetContentEncoding("text/plain")

	Wait For (job) JobDone(j As HttpJob)
   
	If j.Success Then
		
'		Log(j.GetString)
		
		Dim fid As String= ""
		root = JsonParse(j.GetString)
		Dim result As Int = root.Get("start")
		
		If result = 1 Then 'file found
		
			Dim matches As List = root.Get("matches")
			For Each colmatches As Map In matches
				Dim metadata As Map = colmatches.Get("metadata")
				fid = metadata.Get("id")
			Next
			
			If SubExists(evModule, evName & "_FileSearch") Then
			CallSub3(evModule, evName & "_FileSearch", fid,True)
		End If
			
		Else
			
			If SubExists(evModule, evName & "_FileSearch") Then
				CallSub3(evModule, evName & "_FileSearch", fid,False)
			End If
		
		End If
	
		
	
	Else
		
		If SubExists(evModule, evName & "_FileSearch") Then
			CallSub3(evModule, evName & "_FileSearch", Null ,False)
		End If
	
	End If
	
	j.Release
	Return Null
	
	
End Sub

Sub JsonParse(res As String) As Map
	Dim parser As JSONParser
	parser.Initialize(res)
	Dim root As Map = parser.NextObject
	
	Return root
End Sub	


'Get information about the current user's account.
'Example:<code>
'dpx.GetCurrentAccount
'Wait For DropBox_CurrentAccount(meta As Map,success As Boolean)
'If success Then
'	Log("meta is = "&meta)
'	Log($"Email is : ${meta.Get("email")}"$)
'End If
'</code>
Sub GetCurrentAccount() As ResumableSub
	Dim root As Map
	Dim j As HttpJob
	j.Initialize("",Me)
	j.PostString(mApiV2url&"users/get_current_account",Null)
	j.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	j.GetRequest.SetHeader("Content-Type", "application/json")
	j.GetRequest.SetContentType("application/json")
	j.GetRequest.SetContentEncoding("text/plain")

	Wait For (j) JobDone(j As HttpJob)
   
	If j.Success Then
		root = JsonParse(j.GetString)
		If SubExists(evModule, evName & "_CurrentAccount") Then
			CallSub3(evModule, evName & "_CurrentAccount", root,True)
		End If
	Else
       
		If SubExists(evModule, evName & "_CurrentAccount") Then
			CallSub3(evModule, evName & "_CurrentAccount", root,False)
		End If
	End If
	j.Release
	Return Null
   
End Sub


' Create a folder at a given path. 
' example : /test >>>creat folder in app folder
#IgnoreWarnings : 12
Sub CreateFolder(path As String) As ResumableSub
	Dim root As Map
	Dim job As HttpJob
	job.Initialize("",Me)
	job.PostString(mApiV2url&"files/create_folder",$"{"path":"${path}"}"$)
	job.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	job.GetRequest.SetHeader("Content-Type", "application/json")
	job.GetRequest.SetContentType("application/json")
	job.GetRequest.SetContentEncoding("text/plain")

	Wait For (job) JobDone(j As HttpJob)
   
	If j.Success Then
		root = JsonParse(j.GetString)
		If SubExists(evModule, evName & "_CreateFolder") Then
			CallSub3(evModule, evName & "_CreateFolder", root, True)
		End If
	Else
       
		If SubExists(evModule, evName & "_CreateFolder") Then
			CallSub3(evModule, evName & "_CreateFolder", root, False)
		End If
	End If
	j.Release
	Return Null
	
End Sub


'Returns the contents of a root folder. 
Sub ListFiles(path As String) As ResumableSub
	Dim root As Map
	
	Dim job As HttpJob
	job.Initialize("",Me)
	job.Tag = path
	job.PostString(mApiV2url&"files/list_folder",$"{"path":"${path}"}"$)
	job.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	job.GetRequest.SetHeader("Content-Type", "application/json")
	job.GetRequest.SetContentType("application/json")
	job.GetRequest.SetContentEncoding("text/plain")

	Wait For (job) JobDone(j As HttpJob)
   
	If j.Success Then
		
		root = JsonParse(j.GetString)
		If SubExists(evModule, evName & "_ListFiles") Then
			CallSub2(evModule, evName & "_ListFiles", root)
		End If
	Else
       
		If SubExists(evModule, evName & "_ListFiles") Then
			CallSub2(evModule, evName & "_ListFiles", root)
		End If
	End If
	j.Release
	Return Null
	


End Sub

' Create a new file with the contents provided 
' in the request. Do Not use this To Upload a 
' File larger than 150 MB. Instead, create 
' an Upload session with UploadSessionStart. 
Sub Upload(localpath As String, localFilename As String, dstpath As String, dstfilename As String) As ResumableSub
	
	Dim session As Map
	session.Initialize
	session.Put("srcpath",localpath)
	session.Put("srcfile",localFilename)
	session.Put("dstpath",dstpath)
	session.Put("dstfile",dstfilename)
	session.Put("status","init")
	
	Dim serverpath As String = dstpath&"/"&dstfilename
	
	Dim job As HttpJob
	job.Initialize("",Me)
	job.Tag = session
	Log($"{"path":"${File.Combine(dstpath,dstfilename)}", "mode": "overwrite"}"$)
	job.PostFile(mApiV2urlContent&"files/upload",localpath,localFilename)
'	job.GetRequest.SetHeader("Content-Type", "application/octet-stream")
	job.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	job.GetRequest.SetHeader("Dropbox-API-Arg", $"{"path":"${serverpath}","mode": "overwrite"}"$)
	job.GetRequest.SetContentType("application/octet-stream")
	job.GetRequest.SetContentEncoding("text/plain")
	
	Wait For (job) JobDone(j As HttpJob)
   
	If j.Success Then
		Dim root As Map
		root = JsonParse(j.GetString)
		If SubExists(evModule, evName & "_UploadFinished") Then
			CallSub3(evModule, evName & "_UploadFinished", root,True)
		End If
	Else
       
		If SubExists(evModule, evName & "_UploadFinished") Then
			CallSub3(evModule, evName & "_UploadFinished", root,False)
		End If
	End If
	j.Release
	Return Null
	
End Sub


' Download a file from a user's Dropbox. 
Sub DownloadFile(localpath As String, destpath As String, destfile As String) As ResumableSub
	Dim serverpath As String = destpath&"/"&destfile
	
	Dim job As HttpJob
	job.Initialize("",Me)
	job.Tag = serverpath
	Log("Download: "&job.Tag)
	job.Download(mApiV2urlContent&"files/download")
	job.GetRequest.SetHeader("Authorization", "Bearer "&ti.AccessToken)
	job.GetRequest.SetHeader("Dropbox-API-Arg", $"{"path":"${serverpath}"}"$)
	job.GetRequest.SetHeader("Accept","application/vnd.dropbox-cors-hack")
	Wait For (job) JobDone(j As HttpJob)
   
	If j.Success Then
'		Dim dest As String = GetFilename(job.Tag)
'		
'		Dim pth As String = GetPath(job.Tag)
'		Dim fle As String = GetFilename(job.Tag)
'		Log("dest: "&dest)
'		Log("DownloadReady: "&job.Tag)
'		Log("DestPath: "&pth)
'		Log("DestFilename: "&fle)
			
		Dim OutStream As OutputStream
		OutStream = File.OpenOutput(localpath, destfile, False) ' Job.Tag is read to set the Original Filename we specify earlier in the creation of the Job
		File.Copy2(job.GetInputStream,OutStream) ' save the file
		OutStream.Close
		
		Dim session As Map
		session.Initialize
		session.Put("download",serverpath)
		session.Put("Path",localpath)
		session.Put("Filename",destfile)
		
		If SubExists(evModule, evName & "_DownloadFinished") Then
			CallSub3(evModule, evName & "_DownloadFinished", session,True)
		End If
		
	Else
       
		If SubExists(evModule, evName & "_DownloadFinished") Then
			CallSub3(evModule, evName & "_DownloadFinished", session,False)
		End If

	End If
	
	job.Release
	Return Null
	

End Sub