--!movie
--!encoding=utf-8
global $
-- config
global BASEURL
global ALIVE_SEC
global DONWLOAD_DIR
-- IDs
global gMyID
global gReceiverID
global gSenderID
-- upload
global gUploadCh
global gUploadQueue
global gUploadBytes
-- download
global gDownloadFlag
global gDownloadCh
global gDownloadFilename
global gDownloadFilesize
global gDownloadTmpFile
global gDownloadBytes
----------------------------------------
--
----------------------------------------
on startMovie
-- libs
$.import("curl")
$.import("drop")
$.import("file")
$.import("filesystem")
$.import("timer")
-- config
BASEURL = "https://valentin.dasdeck.com/projects/p2p_share/"
ALIVE_SEC = 180
DONWLOAD_DIR = $.PATH&"received\"
-- init globals
gUploadQueue = []
gUploadCh = VOID
gDownloadFlag = FALSE
gMyID = VOID
gReceiverID = VOID
gSenderID = VOID
-- persistant CURL handle for downloads
gDownloadCh = $.curl.init()
gDownloadCh.setProgressCallback(#slotDownloadProgress)
gDownloadCh.setHeaderCallback(#slotDownloadHeader)
gDownloadCh.setOption($.curl.CURLOPT.NOPROGRESS, 0)
gDownloadCh.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
-- implemented by CommandLine xtra
gDownloadTmpFile = getLongPathName(getEnvVar("TMP"))&"\"&random(the maxinteger)&".dat"
gDownloadCh.setDestinationFile(gDownloadTmpFile)
-- window settings
w = 320
h = 240
_movie.stage.title = "P2P Share"
_movie.stage.titlebarOptions.visible = TRUE
_movie.stage.rect = rect(0,0,w,h)
_movie.centerStage = TRUE
_movie.exitLock = TRUE
-- show the window
_movie.stage.visible = 1
connect()
end
----------------------------------------
--
----------------------------------------
on stopMovie
if not voidP(gMyID) then
disconnect()
end if
if not voidP(gDownloadCh) then
gDownloadCh.setDestinationFile()
$.file.delete(gDownloadTmpFile)
end if
end
----------------------------------------
-- @callback
----------------------------------------
on closeRequest
if not (_key.shiftDown) and (not(voidP(gUploadCh)) or gDownloadFlag) then
alert("There are still ongoing file transfers. If you nevertheless want to quit, press the Shift-Key while closing the window.")
else
stopMovie()
_movie.halt()
end if
end
----------------------------------------
--
----------------------------------------
on connect ()
-- status field
m = new(#field)
m.name = "status"
m.rect = rect(0,0,280,40)
m.fontsize = 12
m.alignment = "center"
sprite(5).puppet = TRUE
sprite(5).member = m
sprite(5).loc = point(20,180)
_ch = $.curl.init()
_ch.setOption($.curl.CURLOPT.URL, BASEURL&"?connect")
_ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
res = _ch.exec(1)
if integerP(res) then
member("status").text = "Error:" && curl_error(res)
_movie.updateStage()
else
gMyID = integer(res.readRawString(res.length))
id_str = string(gMyID)
put SPACE after char 6 of id_str
put SPACE after char 3 of id_str
-- create a field
m = new(#field)
m.rect = rect(0,0,280,40)
m.alignment = "center"
m.fontsize = 24
m.fontStyle = "bold"
m.text = "Your ID:" && id_str
sprite(1).puppet = TRUE
sprite(1).member = m
sprite(1).loc = point(20,20)
-- create a field
m = new(#field)
m.rect = rect(0,0,200,40)
m.alignment = "left"
m.fontsize = 14
m.fontStyle = "bold"
m.text = "Receiver-ID:"
sprite(2).puppet = TRUE
sprite(2).member = m
sprite(2).loc = point(60,80)
-- create a field
m = new(#field)
m.name = "Receiver-ID"
m.rect = rect(0,0,200,50)
m.editable = TRUE
m.alignment = "center"
m.boxType = #fixed
m.margin = 2
m.fontsize = 14
m.border = 1
sprite(3).puppet = TRUE
sprite(3).member = m
sprite(3).loc = point(60,100)
-- create a button
m = new(#button)
m.rect = rect(0,0,200,40)
m.text = "Connect to Receiver"
m.alignment = "center"
m.fontsize = 14
m.fontStyle = "bold"
m.scriptText = "on mouseUp"&RETURN&\
" connectToReceiver()"&RETURN&\
"end"
sprite(4).puppet = TRUE
sprite(4).member = m
sprite(4).loc = point(60,140)
-- progress field
m = new(#field)
m.name = "progress"
m.rect = rect(0,0,280,40)
m.fontsize = 12
m.alignment = "center"
sprite(6).puppet = TRUE
sprite(6).member = m
sprite(6).loc = point(20,220)
-- force immediate stage update
_movie.updateStage()
-- create alive timeout
$.timer.create(ALIVE_SEC*1000, #slotSendAlive)
reconnect()
end if
end
----------------------------------------
-- @callback
----------------------------------------
on disconnect ()
_ch = $.curl.init()
_ch.setOption($.curl.CURLOPT.URL, BASEURL&"?disconnect="&gMyID)
_ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
_ch.exec()
end
----------------------------------------
--
----------------------------------------
on reconnect()
if voidP(gDownloadCh) then return
if gDownloadFlag then return
gDownloadCh.setDestinationFile() -- flush
gDownloadCh.setDestinationFile(gDownloadTmpFile)
gDownloadCh.setOption($.curl.CURLOPT.URL, BASEURL&"?id="&gMyID)
-- returnMode: 0=return error code (=default), 1=return data, 2=return chunks immediately
$.curl.execAsyncDetached(gDownloadCh, #slotDownloadResult, _movie, 1)
end
----------------------------------------
-- @callback
----------------------------------------
on slotDownloadResult (res)
if integerP(res) then
member("status").text = "Error:" && curl_error(res)
end if
reconnect()
end
----------------------------------------
-- @callback
----------------------------------------
on connectToReceiver ()
str = member("Receiver-ID").text
if str=EMPTY then return
-- remove spaces
repeat with i = str.length down to 1
if str.char[i]=SPACE then delete char i of str
end repeat
receiver_id = integer(str)
if voidP(receiver_id) then
return alert("You entered an incorrect ID.")
end if
if receiver_id<100000000 OR receiver_id>999999999 then
return alert("You entered an incorrect ID.")
end if
if receiver_id=gMyID then
return alert("You can't connect to yourself.")
end if
-- check if receiver exists
_ch = $.curl.init()
_ch.setOption($.curl.CURLOPT.URL, BASEURL&"?check="&receiver_id)
_ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
res = _ch.exec(1)
if integerP(res) then
put "ERROR:" && curl_error(res)
return
end if
if res.readRawString(res.length)="OK" then
gReceiverID = receiver_id
-- connected to receiver
sprite(2).puppet = FALSE
sprite(3).puppet = FALSE
sprite(4).puppet = FALSE
_movie.updateStage()
sprite(1).member.text = "Connected. Drop a file into this window to send it to your partner."
sprite(1).member.alignment = "left"
sprite(1).member.editable = FALSE
sprite(1).member.fontsize = 12
-- start drop support
$.drop.setCallback(#slotFilesDropped)
$.drop.start()
-- we are now in upload mode, so open download connection not needed anymore
gDownloadCh.setDestinationFile()
$.file.delete(gDownloadTmpFile)
gDownloadCh = VOID
else
alert("No user with this ID currently online.")
end if
end
----------------------------------------
-- @callback
----------------------------------------
on slotSendAlive ()
put "Sending alive message."
_ch = $.curl.init()
_ch.setOption($.curl.CURLOPT.URL, BASEURL&"?alive="&gMyID)
_ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
_ch.exec()
end
----------------------------------------
-- @callback
----------------------------------------
on slotFilesDropped (tItems)
if tItems.folders.count then alert("Dropping folders currently not supported. Drop files.")
if tItems.files.count<1 then return -- no file was dropped
repeat with tLocalFile in tItems.files
gUploadQueue.append(tLocalFile)
end repeat
if voidP(gUploadCh) then nextUpload()
end
----------------------------------------
--
----------------------------------------
on nextUpload ()
gUploadCh = VOID
gUploadBytes = 0
if gUploadQueue.count then
tLocalFile = gUploadQueue[1]
fn = $.file.getFileName(tLocalFile)
member("status").text = "Uploading"&&fn
member("progress").text = "0 %"
-- get a CURL handle (xtra instance)
gUploadCh = $.curl.init()
-- specify options
gUploadCh.setOption($.curl.CURLOPT.URL, BASEURL&"?id="&gMyID&"&rcv="&gReceiverID&"&fn="&fn)
gUploadCh.setOption($.curl.CURLOPT.UPLOAD, 1)
gUploadCh.setOption($.curl.CURLOPT.NOPROGRESS, 0)
gUploadCh.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
-- required for HTTP PUT upload with progress callback
-- otherwise progressCallback isn't called
gUploadCh.setOption($.curl.CURLOPT.INFILESIZE, $.file.size(tLocalFile))
-- upload this script file
gUploadCh.setSourceFile(tLocalFile)
-- set callbacks
gUploadCh.setProgressCallback(#slotUploadProgress)
-- returnMode: 0=return error code (=default), 1=return data, 2=return chunks immediately
$.curl.execAsyncDetached(gUploadCh, #slotUploadResult, _movie, 1)
end if
end
----------------------------------------
-- @callback
----------------------------------------
on slotUploadResult (res)
if integerP(res) then
member("status").text = "Error:" && curl_error(res)
end if
gUploadQueue.deleteAt(1)
nextUpload()
end
----------------------------------------
-- @callback
----------------------------------------
on slotUploadProgress (dltotal, dlnow, ultotal, ulnow)
if ultotal>0 AND ulnow<>gUploadBytes then
gUploadBytes = ulnow
member("progress").text = (ulnow*100/ultotal) && "%"
end if
end
----------------------------------------
-- @callback
----------------------------------------
on slotDownloadProgress (dltotal, dlnow)
gDownloadFlag = (dltotal>0)
if dltotal>0 AND dlnow<>gDownloadBytes then
gDownloadBytes = dlnow
member("progress").text = (dlnow*100/dltotal) && "%"
if dlnow>=dltotal then
gDownloadCh.setDestinationFile()
gDownloadFlag = FALSE
if $.file.size(gDownloadTmpFile)=gDownloadFilesize then
ok = $.filesystem.fileMove (gDownloadTmpFile, DONWLOAD_DIR&gDownloadFilename)
member("status").text = "File '"&gDownloadFilename&"' sucessfully received."
gDownloadCh.setDestinationFile(gDownloadTmpFile)
else
$.file.delete(gDownloadTmpFile)
member("status").text = "Error: File transfer failed."
end if
end if
end if
end
----------------------------------------
-- @callback
----------------------------------------
on slotDownloadHeader (res)
headers = $.curl.parseHeaders(res.readRawString(res.length))
if not voidP(headers["Filename"]) then
if voidP(gSenderID) then
gSenderID = headers["Sender"]
-- now in receiver mode
sprite(2).puppet = FALSE
sprite(3).puppet = FALSE
sprite(4).puppet = FALSE
_movie.updateStage()
sprite(1).member.text = "Sender"&&gSenderID&&"is now connected to you."
sprite(1).member.alignment = "left"
sprite(1).member.editable = FALSE
sprite(1).member.fontsize = 12
end if
gDownloadFilename = headers["Filename"]
gDownloadFilesize = integer(headers["Content-Length"])
gDownloadBytes = 0
member("status").text = "Downloading"&&gDownloadFilename
member("progress").text = "0 %"
end if
end