Skip to content

Commit d7505d5

Browse files
committed
adding Dropbox. Tested on Mac, not tested on Windows, not documented
1 parent 3e89ab1 commit d7505d5

11 files changed

Lines changed: 452 additions & 54 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!-- Type your summary here -->
2+
## Description
3+
4+
Uses https://github.com/dropbox/dbxcli
5+
6+
We use a command line interface tool from Dropbox.
7+
Dropbox wrote it in language "Go", which allows to compile a single binary which could be embedded in your application and deployed with it (Apache License).
8+
9+
If Dropbox changes the API, just download their latest release and replace the binary.
10+
11+
For Mac:
12+
The downloaded binary is not signed, so execution on Mac is limited (you need to launch with Control key pressed). To use and deploy we advise to sign it, and then sign/notarize the embedding 4D application.
13+
14+
Setup on customer computer / Both platforms:
15+
To authorize the binary, you need to execute it once through terminal/console and enter:
16+
{path}dbxcli ls
17+
18+
It will ask for access:
19+
1. Go to https://www.dropbox.com/1/oauth2/authorize?client_id=07o23gulcj8qi69&response_type=code&state=state
20+
2. Click "Allow" (you might have to log in first).
21+
3. Copy the authorization code.
22+
Enter the authorization code here:
23+
xxxx
24+
25+
This needs to be done manually once.
26+
27+
codesign --force --sign "Developer ID Application: 4D Deutschland GmbH" /Users/thomas/Desktop/dbxcli

Documentation/Classes/FileTransfer_curl.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ End if
3636
|[.setActiveMode](#setActiveMode)<p>&nbsp;&nbsp;&nbsp;&nbsp;Switch from default passive mode to active mode.|
3737
|[.setRange](#setRange)<p>&nbsp;&nbsp;&nbsp;&nbsp;Allows to upload/download only a part of a file.|
3838
|[.setCurlPrefix](#setCurlPrefix)<p>&nbsp;&nbsp;&nbsp;&nbsp;Allows to use any additional cURL options.|
39-
|[.setCurlPath](#setCurlPath)<p>&nbsp;&nbsp;&nbsp;&nbsp;Allows to use another cURL installation.|
39+
|[.setPath](#setPath)<p>&nbsp;&nbsp;&nbsp;&nbsp;Allows to use another cURL installation.|
4040
|[.enableProgressData](#enableProgressData)<p>&nbsp;&nbsp;&nbsp;&nbsp;If enabled, result.data will include progress information text.|
4141
|[.setAsyncMode](#setAsyncMode)<p>&nbsp;&nbsp;&nbsp;&nbsp;By default all commands are executed synchronously, meaning the command do not return till execution is completed or a timeout occurred. This allows all command to return the result or execution information..|
4242
|[.stop](#stop)<p>&nbsp;&nbsp;&nbsp;&nbsp;Terminates the execution of a running operation, such as upload or download.|
@@ -352,9 +352,9 @@ See https://curl.se/docs/manpage.html for full list.
352352
#### Example
353353
$ftp.setCurlPrefix("--limit-rate 25M") // limit used bandwidth to 25 Mbit.
354354

355-
## setCurlPath
355+
## setPath
356356

357-
### .setCurlPath(Path: Text)
357+
### .setPath(Path: Text)
358358
|Parameter|Type||Description|
359359
|---------|--- |:---:|------|
360360
|Path|Text|->|Path to local cURL installation|
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!-- Type your summary here -->
2+
## Description
3+
4+
## Example
5+
6+
```4d
7+
Type your example here
8+
```
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
Class constructor()
2+
If (Is macOS:C1572)
3+
This:C1470._return:=Char:C90(10)
4+
$path:=Get 4D folder:C485(Current resources folder:K5:16)+"Dropbox"+Folder separator:K24:12+"dbxcli"
5+
This:C1470._Path:=Convert path system to POSIX:C1106($path)
6+
Else
7+
This:C1470._return:=Char:C90(10) //Char(13)+Char(10)
8+
End if
9+
10+
11+
//MARK: FileTransfer
12+
Function getDirectoryListing($targetpath : Text)->$success : Object
13+
If ($targetpath="")
14+
$targetpath:="/"
15+
End if
16+
$url:="ls -l "+$targetpath
17+
$success:=This:C1470._runWorker($url)
18+
If ($success.success)
19+
// data contains a text based dir listing
20+
This:C1470._parseDirListing($success)
21+
End if
22+
23+
Function upload($sourcepath : Text; $targetpath : Text)->$success : Object
24+
//$sourcepath just file name for local directory, else full path in POSIX syntax
25+
// targetpath is full remote path (starting with /, ending with file name
26+
ASSERT:C1129($sourcepath#""; "source path must not be empty")
27+
ASSERT:C1129($targetpath#""; "target path must not be empty")
28+
29+
$url:="put "+$sourcepath+" "+$targetpath
30+
$success:=This:C1470._runWorker($url)
31+
32+
Function download($sourcepath : Text; $targetpath : Text)->$success : Object
33+
//$sourcepath just file name for local directory, else full path in POSIX syntax
34+
// targetpath is full remote path (starting with /, ending with file name
35+
ASSERT:C1129($sourcepath#""; "source path must not be empty")
36+
ASSERT:C1129($targetpath#""; "target path must not be empty")
37+
38+
$url:="get "+$sourcepath+" "+$targetpath
39+
$success:=This:C1470._runWorker($url)
40+
41+
Function createDirectory($targetpath : Text)->$success : Object
42+
ASSERT:C1129($targetpath#""; "target path must not be empty")
43+
$url:="mkdir "+$targetpath
44+
$success:=This:C1470._runWorker($url)
45+
46+
Function deleteDirectory($targetpath : Text; $force : Boolean)->$success : Object
47+
ASSERT:C1129($targetpath#""; "target path must not be empty")
48+
If ($force)
49+
$url:="rm -f "+$targetpath
50+
Else
51+
$url:="rm "+$targetpath
52+
End if
53+
$success:=This:C1470._runWorker($url)
54+
55+
Function deleteFile($targetpath : Text)->$success : Object
56+
// same as deleteDirectory
57+
ASSERT:C1129($targetpath#""; "target path must not be empty")
58+
$url:="rm "+$targetpath
59+
$success:=This:C1470._runWorker($url)
60+
61+
Function renameFile($sourcepath : Text; $targetpath : Text)->$success : Object
62+
ASSERT:C1129($sourcepath#""; "source path must not be empty")
63+
ASSERT:C1129($targetpath#""; "target path must not be empty")
64+
$url:="mv "+$sourcepath+" "+$targetpath
65+
$success:=This:C1470._runWorker($url)
66+
67+
Function moveFile($sourcepath : Text; $targetpath : Text)->$success : Object
68+
$success:=This:C1470.renameFile($sourcepath; $targetpath)
69+
70+
Function copyFile($sourcepath : Text; $targetpath : Text)->$success : Object
71+
ASSERT:C1129($sourcepath#""; "source path must not be empty")
72+
ASSERT:C1129($targetpath#""; "target path must not be empty")
73+
$url:="cp "+$sourcepath+" "+$targetpath
74+
$success:=This:C1470._runWorker($url)
75+
76+
//MARK: Settings
77+
Function version()->$data : Object
78+
$data:=This:C1470._runWorker("version")
79+
80+
Function setPath($path : Text)
81+
This:C1470._Path:=$path
82+
83+
Function useCallback($callback : 4D:C1709.Function; $ID : Text)
84+
ASSERT:C1129(Value type:C1509($callback)=Is object:K8:27; "Callback must be of type function")
85+
ASSERT:C1129(OB Instance of:C1731($callback; 4D:C1709.Function); "Callback must be of type function")
86+
ASSERT:C1129($ID#""; "Callback ID Method must not be empty")
87+
This:C1470._Callback:=$callback
88+
This:C1470._CallbackID:=$ID
89+
This:C1470._noProgress:=False:C215
90+
91+
// MARK: Internal helper calls
92+
Function _parseDirListing($success : Object)
93+
$col:=Split string:C1554(String:C10($success.data); This:C1470._return; sk ignore empty strings:K86:1)
94+
If (($col.length>0) && ($col[0]="Revision@"))
95+
$success.list:=New collection:C1472
96+
$posSize:=Position:C15("Size"; $col[0])
97+
$posLast:=Position:C15("Last"; $col[0])
98+
$posPath:=Position:C15("Path"; $col[0])
99+
If (($posSize#0) & ($posLast#0) & ($posPath#0))
100+
For each ($line; $col; 1)
101+
$diritem:=New object:C1471
102+
$diritem.revision:=This:C1470._trim(Substring:C12($line; 1; $posSize-1))
103+
$diritem.size:=This:C1470._trim(Substring:C12($line; $posSize; $posLast-$posSize-1))
104+
$diritem.date:=This:C1470._trim(Substring:C12($line; $posLast; $posPath-$posLast-1))
105+
$diritem.path:=This:C1470._trim(Substring:C12($line; $posPath))
106+
$success.list.push($diritem)
107+
End for each
108+
Else // error?
109+
$success.success:=False:C215
110+
$success.responseError:="Directory listing unexpected format"
111+
End if
112+
Else
113+
$success.success:=False:C215
114+
$success.responseError:="Directory listing unexpected format"
115+
End if
116+
117+
Function _runWorker($para : Text)->$result : Object
118+
If (This:C1470._Callback#Null:C1517)
119+
$workerpara:=cs:C1710.SystemWorkerProperties.new(This:C1470.onData; This:C1470._Callback; This:C1470._CallbackID)
120+
Else
121+
$workerpara:=cs:C1710.SystemWorkerProperties.new(This:C1470.onData)
122+
End if
123+
124+
If ((This:C1470._Path) && (This:C1470._Path#""))
125+
$path:=This:C1470._Path
126+
Else
127+
//TODO: needs to be set to use Resource/Mac or /win
128+
$path:="dbxcli"
129+
End if
130+
131+
$command:=$path+" "+$para
132+
$old:=Method called on error:C704
133+
ON ERR CALL:C155(Formula:C1597(ErrorHandler).source)
134+
This:C1470._worker:=4D:C1709.SystemWorker.new($command; $workerpara)
135+
$worker:=This:C1470._worker
136+
137+
If ($worker#Null:C1517)
138+
If ((This:C1470._async#Null:C1517) && (This:C1470._async))
139+
$result:=New object:C1471("data"; "async"; "success"; True:C214)
140+
Else
141+
$worker.wait()
142+
If (($worker.responseError#Null:C1517) && ($worker.responseError#""))
143+
$result:=New object:C1471("responseError"; $worker.responseError; "success"; False:C215)
144+
$pos:=Position:C15("Error:"; $worker.responseError; *)
145+
If ($pos>0)
146+
$result.error:=Replace string:C233(Substring:C12($worker.responseError; $pos+6); Char:C90(10); "")
147+
Else
148+
If (($worker.response#Null:C1517) && ($worker.response#"")) // seems not to be an error, sometime curl set's process bar in error and result in response.
149+
$result:=New object:C1471("data"; $worker.response; "success"; True:C214)
150+
Else
151+
$result:=New object:C1471("data"; $worker.responseError; "success"; True:C214)
152+
End if
153+
End if
154+
Else
155+
$result:=New object:C1471("data"; $worker.response; "success"; True:C214)
156+
End if
157+
End if
158+
Else
159+
$result:=New object:C1471("success"; False:C215; "responseError"; "Curl execution error")
160+
End if
161+
ON ERR CALL:C155($old)
162+
163+
Function _trim($text : Text)->$result : Text
164+
$result:=$text
165+
While (Substring:C12($result; 1; 1)=" ")
166+
$result:=Substring:C12($result; 2)
167+
End while
168+
While (Substring:C12($result; Length:C16($result); 1)=" ")
169+
$result:=Substring:C12($result; 1; Length:C16($result)-1)
170+
End while
171+

Project/Sources/Classes/FileTransfer_curl.4dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Function setCurlPrefix($prefix : Text)
6565
// allows to set any parameters directly after curl
6666
This:C1470._prefix:=$prefix
6767

68-
Function setCurlPath($path : Text)
68+
Function setPath($path : Text)
6969
This:C1470._curlPath:=$path
7070

7171
Function enableProgressData($enable : Boolean)

Project/Sources/Methods/test.4dm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ End if
1111

1212
var $ftp : cs:C1710.FileTransfer_curl
1313
$ftp:=cs:C1710.FileTransfer_curl.new($credentials.url; $credentials.user; $credentials.pass; "ftp")
14-
//$ftp.setCurlPath("/opt/homebrew/opt/curl/bin/curl")
15-
//$ftp.setCurlPath("C:\\Users\\thomas.DE\\Documents\\4D\\Komponenten\\curl.exe")
14+
//$ftp.setPath("/opt/homebrew/opt/curl/bin/curl")
15+
//$ftp.setPath("C:\\Users\\thomas.DE\\Documents\\4D\\Komponenten\\curl.exe")
1616
$ftp.setConnectTimeout(5)
1717

1818
If (False:C215)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//%attributes = {}
2+
var $ftp : cs:C1710.FileTransfer_Dropbox
3+
$ftp:=cs:C1710.FileTransfer_Dropbox.new()
4+
//$ftp.setPath(" /Users/thomas/Desktop/dbxcli")
5+
6+
7+
If (False:C215)
8+
$source:="/"
9+
$result:=$ftp.getDirectoryListing($source)
10+
If ($result.success)
11+
$answer:=$result.data
12+
End if
13+
End if
14+
15+
16+
// upload große datei, progress bar - Windows
17+
If (False:C215)
18+
$ftp.useCallback(Formula:C1597(ProgressCallback); "ProgressCallback")
19+
$source:=System folder:C487(Desktop:K41:16)+"Heap.pdf"
20+
$source:=Convert path system to POSIX:C1106($source)
21+
$target:="/Firma/test2.pdf"
22+
$result:=$ftp.upload($source; $target)
23+
If ($result.success)
24+
$answer:=$result.data
25+
End if
26+
End if
27+
28+
If (False:C215)
29+
$ftp.useCallback(Formula:C1597(ProgressCallback); "ProgressCallback")
30+
$target:=System folder:C487(Desktop:K41:16)+"result.pdf"
31+
$target:=Convert path system to POSIX:C1106($target)
32+
$source:="/Firma/test2.pdf"
33+
$result:=$ftp.download($source; $target)
34+
If ($result.success)
35+
$answer:=$result.data
36+
End if
37+
End if
38+
39+
40+
If (False:C215)
41+
$target:="/Firma/newdir"
42+
$result:=$ftp.createDirectory($target)
43+
If ($result.success)
44+
$answer:=$result.data
45+
End if
46+
End if
47+
48+
If (True:C214)
49+
$target:="/Firma/newdir"
50+
$result:=$ftp.deleteDirectory($target; True:C214) // delete even with content
51+
If ($result.success)
52+
$answer:=$result.data
53+
End if
54+
End if
55+
56+
If (True:C214)
57+
$target:="/Firma/test2.pdf"
58+
$result:=$ftp.deleteFile($target)
59+
If ($result.success)
60+
$answer:=$result.data
61+
End if
62+
End if

0 commit comments

Comments
 (0)