Thursday, November 25, 2010 at 12:05 AM.
system.verbs.builtins.tcp.ftp.writeFile
on writeFile (adrconnectiontable, filetext, path) { <<Upload a file via FTP. <<Changes: <<09/06/00; 2:35:03 PM by PBS <<Fix for "dataport not defined" bug. local (response); local (adrlistens = @system.temp.ftpClientListens); if not defined (adrlistens^) { new (tabletype, adrlistens)}; local (adrdaemon = @tcp.ftp.daemon); on fatal (errmsg) { <<tcp.ftp.quit () <<delete (adrconnectiontable) tcp.ftp.closeConnection (adrconnectiontable); scriptError (errmsg)}; on sureDirectory (directory) { if directory == adrconnectiontable^.currentHostDirectory { // we're already there! return}; response = tcp.ftp.sendCommand (adrconnectiontable, "CWD " + directory); // can we get there now? if not (response beginsWith "2") { // failure local (i, level=string.countFields (directory, '/')); tcp.ftp.sendCommand (adrconnectiontable, "CWD " + "/"); // start at root adrconnectiontable^.currentHostDirectory = ""; for i = 1 to level { local (tempdir = string.nthField (directory, '/', i)); if tempdir == "" { // this is the root; skip it continue}; response = tcp.ftp.sendCommand (adrconnectiontable, "CWD " + tempdir); if response beginsWith "550" { response = tcp.ftp.sendCommand (adrconnectiontable, "MKD " + tempdir); if response beginsWith "550" { // we can't make the directory, possibly permissions error scriptError (response)}; tcp.ftp.sendCommand (adrconnectiontable, "CWD " + tempdir)}; adrconnectiontable^.currentHostDirectory = adrconnectiontable^.currentHostDirectory + "/" + tempdir}}; response = tcp.ftp.sendCommand (adrconnectiontable, "PWD"); adrconnectiontable^.currentHostDirectory = string.nthField (response, '"', 2)}; on openActiveStream () { local { port; hi; lo; portString; start = clock.now()}; loop { if clock.now() > (start + 60) { // don't wait forever! scriptError ("Timeout trying to find available data stream port")}; semaphore.lock (this, 3600); port = random (1025, 1100); //should be legal range if defined (adrlistens^.[port]) { //this port already in use semaphore.unlock (this); continue} else { new (tableType, @adrlistens^.[port]); semaphore.unlock (this); break}}; adrlistens^.[port].ready = false; adrlistens^.[port].listenID = tcp.listenStream (port, 1, adrdaemon, port); hi = port / 256; lo = port % 256; portString = string.replaceAll (tcp.addressdecode(tcp.myaddress()), '.', ',') + "," + string(hi) + "," + string(lo); tcp.ftp.sendCommand (adrconnectiontable, "PORT " + portString); return (port)}; on openPassiveStream (portstring) { local { ipAddr; hi; lo}; if portstring contains '(' { // port information is between matching parentheses portstring = string.delete (portstring, 1, string.patternMatch ("(", portstring)); portstring = string.delete (portstring, string.patternMatch (')', portstring), infinity); portstring = string.popLeading (portstring, '"'); portstring = string.popTrailing (portstring, '"')} else { // brute force way, searching for commas local (leftHalf); local (firstComma); firstComma = string.patternMatch (',', portstring); // find first comma if firstComma == 0 { // oops! we're in big trouble! scriptError ("Can't determine passive port number! " + portstring)}; leftHalf = string.mid (portstring, 1, firstComma-1); // the first number we want is at the end of leftHalf for i = sizeOf (leftHalf) downTo 1 { if not string.isnumeric (leftHalf[i]) { // we've found a non-numeric break}}; portstring = string.delete (portstring, 1, i)}; // chop off all but number ipAddr = string.nthField (portstring, ',', 1); ipAddr = ipAddr + "." + string.nthField (portstring, ',', 2); ipAddr = ipAddr + "." + string.nthField (portstring, ',', 3); ipAddr = ipAddr + "." + string.nthField (portstring, ',', 4); hi = number (string.nthField (portstring, ',', 5)) * 256; lo = number (string.nthField (portstring, ',', 6)); return (tcp.openStream (tcp.addressEncode(ipAddr), hi+lo))}; on stopListen (port) { tcp.closeListen (adrlistens^.[port].listenID); delete (@adrlistens^.[port])}; on waitForListen (port, timeout=60) { local (start = clock.now()); loop { if clock.now() > start+timeout { fatal ("Timeout waiting for FTP data connection")}; if adrlistens^.[port].ready { local (temp); temp = adrlistens^.[port].stream; stopListen (port); return (temp)}; tcp.ftp.yieldProcessor ()}}; local (filename); bundle { //set filename, possibly modify the path if path contains "/" { filename = string.nthfield (path, "/", string.countfields (path, "/")); path = string.mid (path, 1, sizeof (path) - sizeof (filename))} else { filename = path; path = ""}}; sureDirectory (path); loop { // I deal with the "426" error on Mac by re-sending the file local (isActive=false); local (dataStream, dataPort); tcp.ftp.sendCommand (adrconnectiontable, "TYPE I"); response = tcp.ftp.sendCommand (adrconnectiontable,"PASV"); // try to use passive mode if response beginsWith "2" { dataStream = openPassiveStream (response)} else { // no PASV support from host dataStream = openActiveStream (); // returns immediately with listenRef isActive = true}; loop { // we repeat this sequence if the host is not quite ready yet response = tcp.ftp.sendCommand (adrconnectiontable, "STOR " + filename); case true { response beginsWith "425" { <<clock.waitSeconds (2) thread.sleepFor (2); continue}; response beginsWith "5" { tcp.ftp.sendCommand (adrconnectiontable, "ABOR"); // abort the stream attempt if isActive { dataStream = waitForListen (dataPort)}; try { tcp.closeStream (dataStream) }; fatal (response)}} else { break}}; if isActive { dataStream = waitForListen (dataPort)}; if sizeOf (filetext) > 0 { // if any data left tcp.writeStream (dataStream, filetext)}; tcp.closeStream (dataStream); response = tcp.ftp.readResponse (adrconnectiontable); if not (response beginsWith "2") { // anything other than 2xx is probably error if response beginsWith "426" { // we're currently seeing this on Mac continue}; // it's not fatal so we try again fatal (response)} else { break}}}
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.