1. --!movie
  2. --!encoding=utf-8
  3.  
  4. global $
  5.  
  6. -- config
  7. global BASEURL
  8. global ALIVE_SEC
  9. global DONWLOAD_DIR
  10.  
  11. -- IDs
  12. global gMyID
  13. global gReceiverID
  14. global gSenderID
  15.  
  16. -- upload
  17. global gUploadCh
  18. global gUploadQueue
  19. global gUploadBytes
  20.  
  21. -- download
  22. global gDownloadFlag
  23. global gDownloadCh
  24. global gDownloadFilename
  25. global gDownloadFilesize
  26. global gDownloadTmpFile
  27. global gDownloadBytes
  28.  
  29. ----------------------------------------
  30. --
  31. ----------------------------------------
  32. on startMovie
  33.  
  34.   -- libs
  35.   $.import("curl")
  36.   $.import("drop")
  37.   $.import("file")
  38.   $.import("filesystem")
  39.   $.import("timer")
  40.  
  41.   -- config
  42.   BASEURL = "https://valentin.dasdeck.com/projects/p2p_share/"
  43.   ALIVE_SEC = 180
  44.   DONWLOAD_DIR = $.PATH&"received\"
  45.  
  46.   -- init globals
  47.   gUploadQueue = []
  48.   gUploadCh = VOID
  49.   gDownloadFlag = FALSE
  50.  
  51.   gMyID = VOID
  52.   gReceiverID = VOID
  53.   gSenderID = VOID
  54.  
  55.   -- persistant CURL handle for downloads
  56.   gDownloadCh = $.curl.init()
  57.   gDownloadCh.setProgressCallback(#slotDownloadProgress)
  58.   gDownloadCh.setHeaderCallback(#slotDownloadHeader)
  59.   gDownloadCh.setOption($.curl.CURLOPT.NOPROGRESS, 0)
  60.   gDownloadCh.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  61.  
  62.   -- implemented by CommandLine xtra
  63.   gDownloadTmpFile = getLongPathName(getEnvVar("TMP"))&"\"&random(the maxinteger)&".dat"
  64.   gDownloadCh.setDestinationFile(gDownloadTmpFile)
  65.  
  66.   -- window settings
  67.   w = 320
  68.   h = 240
  69.   _movie.stage.title = "P2P Share"
  70.   _movie.stage.titlebarOptions.visible = TRUE
  71.   _movie.stage.rect = rect(0,0,w,h)
  72.   _movie.centerStage = TRUE
  73.   _movie.exitLock = TRUE
  74.  
  75.   -- show the window
  76.   _movie.stage.visible = 1
  77.  
  78.   connect()
  79. end
  80.  
  81. ----------------------------------------
  82. --
  83. ----------------------------------------
  84. on stopMovie
  85.   if not voidP(gMyID) then
  86.     disconnect()
  87.   end if
  88.   if not voidP(gDownloadCh) then
  89.     gDownloadCh.setDestinationFile()
  90.     $.file.delete(gDownloadTmpFile)
  91.   end if
  92. end
  93.  
  94. ----------------------------------------
  95. -- @callback
  96. ----------------------------------------
  97. on closeRequest
  98.   if not (_key.shiftDown) and (not(voidP(gUploadCh)) or gDownloadFlag) then
  99.     alert("There are still ongoing file transfers. If you nevertheless want to quit, press the Shift-Key while closing the window.")
  100.   else
  101.     stopMovie()
  102.     _movie.halt()
  103.   end if
  104. end
  105.  
  106. ----------------------------------------
  107. --
  108. ----------------------------------------
  109. on connect ()
  110.  
  111.   -- status field
  112.   m = new(#field)
  113.   m.name = "status"
  114.   m.rect = rect(0,0,280,40)
  115.   m.fontsize = 12
  116.   m.alignment = "center"
  117.   sprite(5).puppet = TRUE
  118.   sprite(5).member = m
  119.   sprite(5).loc = point(20,180)
  120.  
  121.   _ch = $.curl.init()
  122.   _ch.setOption($.curl.CURLOPT.URL, BASEURL&"?connect")
  123.   _ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  124.   res = _ch.exec(1)
  125.  
  126.   if integerP(res) then
  127.     member("status").text = "Error:" && curl_error(res)
  128.     _movie.updateStage()
  129.   else
  130.     gMyID = integer(res.readRawString(res.length))
  131.     id_str = string(gMyID)
  132.     put SPACE after char 6 of id_str
  133.     put SPACE after char 3 of id_str
  134.  
  135.     -- create a field
  136.     m = new(#field)
  137.     m.rect = rect(0,0,280,40)
  138.     m.alignment = "center"
  139.     m.fontsize = 24
  140.     m.fontStyle = "bold"
  141.     m.text = "Your ID:" && id_str
  142.     sprite(1).puppet = TRUE
  143.     sprite(1).member = m
  144.     sprite(1).loc = point(20,20)
  145.  
  146.     -- create a field
  147.     m = new(#field)
  148.     m.rect = rect(0,0,200,40)
  149.     m.alignment = "left"
  150.     m.fontsize = 14
  151.     m.fontStyle = "bold"
  152.     m.text = "Receiver-ID:"
  153.     sprite(2).puppet = TRUE
  154.     sprite(2).member = m
  155.     sprite(2).loc = point(60,80)
  156.  
  157.     -- create a field
  158.     m = new(#field)
  159.     m.name = "Receiver-ID"
  160.     m.rect = rect(0,0,200,50)
  161.     m.editable = TRUE
  162.     m.alignment = "center"
  163.     m.boxType = #fixed
  164.     m.margin = 2
  165.     m.fontsize = 14
  166.     m.border = 1
  167.     sprite(3).puppet = TRUE
  168.     sprite(3).member = m
  169.     sprite(3).loc = point(60,100)
  170.  
  171.     -- create a button
  172.     m = new(#button)
  173.     m.rect = rect(0,0,200,40)
  174.     m.text = "Connect to Receiver"
  175.     m.alignment = "center"
  176.     m.fontsize = 14
  177.     m.fontStyle = "bold"
  178.     m.scriptText = "on mouseUp"&RETURN&\
  179.     "  connectToReceiver()"&RETURN&\
  180.     "end"
  181.     sprite(4).puppet = TRUE
  182.     sprite(4).member = m
  183.     sprite(4).loc = point(60,140)
  184.  
  185.     -- progress field
  186.     m = new(#field)
  187.     m.name = "progress"
  188.     m.rect = rect(0,0,280,40)
  189.     m.fontsize = 12
  190.     m.alignment = "center"
  191.     sprite(6).puppet = TRUE
  192.     sprite(6).member = m
  193.     sprite(6).loc = point(20,220)
  194.  
  195.     -- force immediate stage update
  196.     _movie.updateStage()
  197.  
  198.     -- create alive timeout
  199.     $.timer.create(ALIVE_SEC*1000, #slotSendAlive)
  200.  
  201.     reconnect()
  202.   end if
  203. end
  204.  
  205. ----------------------------------------
  206. -- @callback
  207. ----------------------------------------
  208. on disconnect ()
  209.   _ch = $.curl.init()
  210.   _ch.setOption($.curl.CURLOPT.URL, BASEURL&"?disconnect="&gMyID)
  211.   _ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  212.   _ch.exec()
  213. end
  214.  
  215. ----------------------------------------
  216. --
  217. ----------------------------------------
  218. on reconnect()
  219.   if voidP(gDownloadCh) then return
  220.   if gDownloadFlag then return
  221.  
  222.   gDownloadCh.setDestinationFile() -- flush
  223.   gDownloadCh.setDestinationFile(gDownloadTmpFile)
  224.  
  225.   gDownloadCh.setOption($.curl.CURLOPT.URL, BASEURL&"?id="&gMyID)
  226.  
  227.   -- returnMode: 0=return error code (=default), 1=return data, 2=return chunks immediately
  228.   $.curl.execAsyncDetached(gDownloadCh, #slotDownloadResult, _movie, 1)
  229. end
  230.  
  231. ----------------------------------------
  232. -- @callback
  233. ----------------------------------------
  234. on slotDownloadResult (res)
  235.   if integerP(res) then
  236.     member("status").text = "Error:" && curl_error(res)
  237.   end if
  238.   reconnect()
  239. end
  240.  
  241. ----------------------------------------
  242. -- @callback
  243. ----------------------------------------
  244. on connectToReceiver ()
  245.   str = member("Receiver-ID").text
  246.   if str=EMPTY then return
  247.  
  248.   -- remove spaces
  249.   repeat with i = str.length down to 1
  250.     if str.char[i]=SPACE then delete char i of str
  251.   end repeat
  252.  
  253.   receiver_id = integer(str)
  254.  
  255.   if voidP(receiver_id) then
  256.     return alert("You entered an incorrect ID.")
  257.   end if
  258.  
  259.   if receiver_id<100000000 OR receiver_id>999999999 then
  260.     return alert("You entered an incorrect ID.")
  261.   end if
  262.  
  263.   if receiver_id=gMyID then
  264.     return alert("You can't connect to yourself.")
  265.   end if
  266.  
  267.   -- check if receiver exists
  268.   _ch = $.curl.init()
  269.   _ch.setOption($.curl.CURLOPT.URL, BASEURL&"?check="&receiver_id)
  270.   _ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  271.   res = _ch.exec(1)
  272.   if integerP(res) then
  273.     put "ERROR:" && curl_error(res)
  274.     return
  275.   end if
  276.   if res.readRawString(res.length)="OK" then
  277.  
  278.     gReceiverID = receiver_id
  279.  
  280.     -- connected to receiver
  281.     sprite(2).puppet = FALSE
  282.     sprite(3).puppet = FALSE
  283.     sprite(4).puppet = FALSE
  284.     _movie.updateStage()
  285.  
  286.     sprite(1).member.text = "Connected. Drop a file into this window to send it to your partner."
  287.     sprite(1).member.alignment = "left"
  288.     sprite(1).member.editable = FALSE
  289.     sprite(1).member.fontsize = 12
  290.  
  291.     -- start drop support
  292.     $.drop.setCallback(#slotFilesDropped)
  293.     $.drop.start()
  294.  
  295.     -- we are now in upload mode, so open download connection not needed anymore
  296.     gDownloadCh.setDestinationFile()
  297.     $.file.delete(gDownloadTmpFile)
  298.     gDownloadCh = VOID
  299.  
  300.   else
  301.     alert("No user with this ID currently online.")
  302.   end if
  303. end
  304.  
  305. ----------------------------------------
  306. -- @callback
  307. ----------------------------------------
  308. on slotSendAlive ()
  309.   put "Sending alive message."
  310.   _ch = $.curl.init()
  311.   _ch.setOption($.curl.CURLOPT.URL, BASEURL&"?alive="&gMyID)
  312.   _ch.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  313.   _ch.exec()
  314. end
  315.  
  316. ----------------------------------------
  317. -- @callback
  318. ----------------------------------------
  319. on slotFilesDropped (tItems)
  320.   if tItems.folders.count then alert("Dropping folders currently not supported. Drop files.")
  321.   if tItems.files.count<1 then return -- no file was dropped
  322.  
  323.   repeat with tLocalFile in tItems.files
  324.     gUploadQueue.append(tLocalFile)
  325.   end repeat
  326.  
  327.   if voidP(gUploadCh) then nextUpload()
  328. end
  329.  
  330. ----------------------------------------
  331. --
  332. ----------------------------------------
  333. on nextUpload ()
  334.   gUploadCh = VOID
  335.   gUploadBytes = 0
  336.   if gUploadQueue.count then
  337.     tLocalFile = gUploadQueue[1]
  338.  
  339.     fn = $.file.getFileName(tLocalFile)
  340.     member("status").text = "Uploading"&&fn
  341.     member("progress").text = "0 %"
  342.  
  343.     -- get a CURL handle (xtra instance)
  344.     gUploadCh = $.curl.init()
  345.  
  346.     -- specify options
  347.     gUploadCh.setOption($.curl.CURLOPT.URL, BASEURL&"?id="&gMyID&"&rcv="&gReceiverID&"&fn="&fn)
  348.     gUploadCh.setOption($.curl.CURLOPT.UPLOAD, 1)
  349.     gUploadCh.setOption($.curl.CURLOPT.NOPROGRESS, 0)
  350.     gUploadCh.setOption($.curl.CURLOPT.SSL_VERIFYPEER, 0)
  351.  
  352.     -- required for HTTP PUT upload with progress callback
  353.     -- otherwise progressCallback isn't called
  354.     gUploadCh.setOption($.curl.CURLOPT.INFILESIZE, $.file.size(tLocalFile))
  355.  
  356.     -- upload this script file
  357.     gUploadCh.setSourceFile(tLocalFile)
  358.  
  359.     -- set callbacks
  360.     gUploadCh.setProgressCallback(#slotUploadProgress)
  361.  
  362.     -- returnMode: 0=return error code (=default), 1=return data, 2=return chunks immediately
  363.     $.curl.execAsyncDetached(gUploadCh, #slotUploadResult, _movie, 1)
  364.   end if
  365. end
  366.  
  367. ----------------------------------------
  368. -- @callback
  369. ----------------------------------------
  370. on slotUploadResult (res)
  371.   if integerP(res) then
  372.     member("status").text = "Error:" && curl_error(res)
  373.   end if
  374.   gUploadQueue.deleteAt(1)
  375.   nextUpload()
  376. end
  377.  
  378. ----------------------------------------
  379. -- @callback
  380. ----------------------------------------
  381. on slotUploadProgress (dltotal, dlnow, ultotal, ulnow)
  382.   if ultotal>0 AND ulnow<>gUploadBytes then
  383.     gUploadBytes = ulnow
  384.     member("progress").text = (ulnow*100/ultotal) && "%"
  385.   end if
  386. end
  387.  
  388. ----------------------------------------
  389. -- @callback
  390. ----------------------------------------
  391. on slotDownloadProgress (dltotal, dlnow)
  392.   gDownloadFlag = (dltotal>0)
  393.   if dltotal>0 AND dlnow<>gDownloadBytes then
  394.     gDownloadBytes = dlnow
  395.     member("progress").text = (dlnow*100/dltotal) && "%"
  396.     if dlnow>=dltotal then
  397.       gDownloadCh.setDestinationFile()
  398.       gDownloadFlag = FALSE
  399.       if $.file.size(gDownloadTmpFile)=gDownloadFilesize then
  400.         ok = $.filesystem.fileMove (gDownloadTmpFile, DONWLOAD_DIR&gDownloadFilename)
  401.         member("status").text = "File '"&gDownloadFilename&"' sucessfully received."
  402.         gDownloadCh.setDestinationFile(gDownloadTmpFile)
  403.       else
  404.         $.file.delete(gDownloadTmpFile)
  405.         member("status").text = "Error: File transfer failed."
  406.       end if
  407.     end if
  408.   end if
  409. end
  410.  
  411. ----------------------------------------
  412. -- @callback
  413. ----------------------------------------
  414. on slotDownloadHeader (res)
  415.   headers = $.curl.parseHeaders(res.readRawString(res.length))
  416.   if not voidP(headers["Filename"]) then
  417.  
  418.     if voidP(gSenderID) then
  419.       gSenderID = headers["Sender"]
  420.  
  421.       -- now in receiver mode
  422.       sprite(2).puppet = FALSE
  423.       sprite(3).puppet = FALSE
  424.       sprite(4).puppet = FALSE
  425.       _movie.updateStage()
  426.  
  427.       sprite(1).member.text = "Sender"&&gSenderID&&"is now connected to you."
  428.       sprite(1).member.alignment = "left"
  429.       sprite(1).member.editable = FALSE
  430.       sprite(1).member.fontsize = 12
  431.  
  432.     end if
  433.  
  434.     gDownloadFilename = headers["Filename"]
  435.     gDownloadFilesize = integer(headers["Content-Length"])
  436.     gDownloadBytes = 0
  437.     member("status").text = "Downloading"&&gDownloadFilename
  438.     member("progress").text = "0 %"
  439.   end if
  440. end
  441.  
[raw code]