@@ -98,34 +98,68 @@ void uploadWithStou(const std::string& filePath,
9898 + scriptPath + " \" " ;
9999
100100#else
101- // ----- Linux / macOS: use the standard ftp client -----
102- // The `sunique` command tells the ftp client to use STOU instead of
103- // STOR when executing `put`, so the server picks a unique filename.
104- // We write the commands to a temp script file and pipe it via `-n`
105- // (no auto-login) to avoid interactive prompts.
101+ // ----- Linux / macOS -----
102+ // macOS removed the built-in `ftp` client, so we check at runtime:
103+ // 1. `ftp` — available on most Linux distros; uses `sunique` + `put`
104+ // 2. `python3` — fallback using ftplib.storbinary('STOU ...'), works everywhere
106105 std::string portStr = std::to_string (port);
107-
108- std::string ftpScript;
109- ftpScript += " open " + host + " " + portStr + " \n " ;
110- ftpScript += " user anonymous anonymous@\n " ;
111- ftpScript += " binary\n " ;
112- ftpScript += " sunique\n " ;
113- ftpScript += " put " + filePath + " \n " ;
114- ftpScript += " bye\n " ;
115-
116- // Write script to a temp file
117- std::string scriptPath = " /tmp/ftp_stou_upload.ftp" ;
118- {
119- std::ofstream scriptFile (scriptPath);
120- if (!scriptFile.is_open ()) {
121- std::cerr << " Failed to create temp script: " << scriptPath << " \n " ;
122- return ;
106+ std::string scriptPath = " /tmp/ftp_stou_upload_script" ;
107+ std::string cmd;
108+
109+ // Check if the `ftp` command exists
110+ if (system (" command -v ftp > /dev/null 2>&1" ) == 0 ) {
111+ // --- Use the standard ftp client with sunique ---
112+ std::string ftpScript;
113+ ftpScript += " open " + host + " " + portStr + " \n " ;
114+ ftpScript += " user anonymous anonymous@\n " ;
115+ ftpScript += " binary\n " ;
116+ ftpScript += " sunique\n " ;
117+ ftpScript += " put " + filePath + " \n " ;
118+ ftpScript += " bye\n " ;
119+
120+ scriptPath += " .ftp" ;
121+ {
122+ std::ofstream f (scriptPath);
123+ if (!f.is_open ()) {
124+ std::cerr << " Failed to create temp script: " << scriptPath << " \n " ;
125+ return ;
126+ }
127+ f << ftpScript;
123128 }
124- scriptFile << ftpScript;
129+ // -n = no auto-login (we login manually via the "user" command)
130+ cmd = " ftp -n < \" " + scriptPath + " \" " ;
131+
125132 }
133+ else if (system (" command -v python3 > /dev/null 2>&1" ) == 0 ) {
134+ // --- Fallback: Python 3 ftplib (available on macOS) ---
135+ std::string pyScript;
136+ pyScript += " import sys\n " ;
137+ pyScript += " from ftplib import FTP\n " ;
138+ pyScript += " ftp = FTP()\n " ;
139+ pyScript += " ftp.connect('" + host + " ', " + portStr + " )\n " ;
140+ pyScript += " ftp.login('anonymous', 'anonymous@')\n " ;
141+ pyScript += " ftp.set_pasv(True)\n " ;
142+ pyScript += " with open('" + filePath + " ', 'rb') as f:\n " ;
143+ pyScript += " result = ftp.storbinary('STOU " + filePath + " ', f)\n " ;
144+ pyScript += " print(result)\n " ;
145+ pyScript += " ftp.quit()\n " ;
146+
147+ scriptPath += " .py" ;
148+ {
149+ std::ofstream f (scriptPath);
150+ if (!f.is_open ()) {
151+ std::cerr << " Failed to create temp script: " << scriptPath << " \n " ;
152+ return ;
153+ }
154+ f << pyScript;
155+ }
156+ cmd = " python3 \" " + scriptPath + " \" " ;
126157
127- // -n = no auto-login (we login manually via the "user" command)
128- std::string cmd = " ftp -n < \" " + scriptPath + " \" " ;
158+ }
159+ else {
160+ std::cerr << " Error: neither 'ftp' nor 'python3' found on this system.\n " ;
161+ return ;
162+ }
129163#endif
130164
131165 std::cout << " Running command:\n " << cmd << " \n\n " ;
0 commit comments