net.sourceforge.plantuml.ftp.FtpLoop Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml-epl Show documentation
Show all versions of plantuml-epl Show documentation
PlantUML is a component that allows to quickly write diagrams from text.
// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
* |
* | PlantUML : a free UML diagram generator
* |
* +=======================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/liberapay (only 1€ per month!)
* https://plantuml.com/paypal
*
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the Eclipse Public License.
*
* THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
* LICENSE ("AGREEMENT"). [Eclipse Public License - v 1.0]
*
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*
* You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* PlantUML can occasionally display sponsored or advertising messages. Those
* messages are usually generated on welcome or error images and never on
* functional diagrams.
* See https://plantuml.com/professional if you want to remove them
*
* Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
* are owned by the author of their corresponding sources code (that is, their
* textual description in PlantUML language). Those images are not covered by
* this EPL license.
*
* The generated images can then be used without any reference to the EPL license.
* It is not even necessary to stipulate that they have been generated with PlantUML,
* although this will be appreciated by the PlantUML team.
*
* There is an exception : if the textual description in PlantUML language is also covered
* by any license, then the generated images are logically covered
* by the very same license.
*
* This is the IGY distribution (Install GraphViz by Yourself).
* You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
* (see https://plantuml.com/graphviz-dot )
*
* Icons provided by OpenIconic : https://useiconic.com/open
* Archimate sprites provided by Archi : http://www.archimatetool.com
* Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
* Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
* ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
* ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
* CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
* Brotli (c) by the Brotli Authors https://github.com/google/brotli
* Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
* Twemoji (c) by Twitter at https://twemoji.twitter.com/
*
*/
package net.sourceforge.plantuml.ftp;
// server
// FtpServer.java
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.StringTokenizer;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.security.SecurityUtils;
class FtpLoop implements Runnable {
// ::remove folder when __HAXE__
enum Mode {
ACTIF, PASSIF
};
final private Socket incoming;
final private FtpServer ftpServer;
final private BufferedReader br;
final private PrintWriter pw;
private FtpConnexion connexion;
private String ipClient = null;
private int port = -1;
private Mode mode;
public FtpLoop(Socket socket, FtpServer ftpServer) throws IOException {
this.incoming = socket;
this.ftpServer = ftpServer;
this.br = new BufferedReader(new InputStreamReader(incoming.getInputStream(), ftpServer.getCharset()));
this.pw = SecurityUtils.createPrintWriter(incoming.getOutputStream(), true);
}
// http://www.ncftp.com/libncftp/doc/ftp_overview.html
// http://www.nsftools.com/tips/RawFTP.htm
// http://www.commentcamarche.net/contents/internet/ftp.php3
// http://en.wikipedia.org/wiki/List_of_FTP_server_return_codes
// http://www.freefire.org/articles/ftpexample.php
// http://forum.hardware.fr/hfr/Programmation/VB-VBA-VBS/transfert-sujet_59989_1.htm
// http://www.excel-downloads.com/forum/104130-telechargement-ftp-via-vba.html
// http://www.pcreview.co.uk/forums/ftp-vba-macro-t949945.html
private void runInternal() throws IOException, InterruptedException {
localLog("Starting Loop");
myOut("220 PlantUML");
while (true) {
final String s = br.readLine();
localLog("s=" + s);
if (s == null) {
pw.close();
br.close();
return;
}
final boolean finish = manage(s);
if (finish) {
return;
}
}
}
private boolean manage(final String cmd) throws UnknownHostException, IOException, InterruptedException {
final String upper = StringUtils.goUpperCase(cmd);
if (upper.startsWith("USER")) {
myOut("331 Password required");
final String user = cmd.substring("USER ".length());
connexion = ftpServer.getFtpConnexion(user);
} else if (upper.startsWith("PASS")) {
myOut("230 Logged in.");
} else if (upper.startsWith("PWD")) {
// myOut("/");
// myOut("200 OK /");
myOut("257 \"/\" is current directory.");
} else if (upper.startsWith("CWD")) {
final String dir = cmd.substring("CWD ".length());
myOut("250 \"" + dir + "\" is new working directory..");
} else if (upper.startsWith("TYPE")) {
myOut("200 Command okay.");
// localLog("type=" + s);
} else if (upper.startsWith("PORT")) {
mode = Mode.ACTIF;
final StringTokenizer st = new StringTokenizer(cmd, " ,");
st.nextToken();
ipClient = st.nextToken() + "." + st.nextToken() + "." + st.nextToken() + "." + st.nextToken();
port = Integer.parseInt(st.nextToken()) * 256 + Integer.parseInt(st.nextToken());
// localLog("ipClient=" + ipClient);
// localLog("port=" + port);
myOut("200 Command okay.");
} else if (upper.startsWith("LIST")) {
if (mode == Mode.ACTIF) {
listActif();
} else {
listPassif();
}
} else if (upper.startsWith("STOR")) {
if (mode == Mode.ACTIF) {
storActif(cmd);
} else {
storPassif(cmd);
}
} else if (upper.startsWith("PASV")) {
mode = Mode.PASSIF;
port = ftpServer.getFreePort();
final int p1 = port / 256;
final int p2 = port % 256;
assert port == p1 * 256 + p2;
localLog("adr=" + incoming.getInetAddress().getHostAddress());
final String ipServer = ftpServer.getIpServer();
localLog("server=" + ipServer);
myOut("227 Entering Passive Mode (" + ipServer.replace('.', ',') + "," + p1 + "," + p2 + ").");
ipClient = ipServer;
} else if (upper.startsWith("RETR")) {
if (mode == Mode.ACTIF) {
retrActif(cmd);
} else {
retrPassif(cmd);
}
} else if (upper.startsWith("DELE")) {
final String file = cmd.substring("DELE ".length());
connexion.delete(file);
myOut("200 Command okay.");
} else if (upper.startsWith("QUIT")) {
myOut("221 Goodbye.");
return true;
} else if (upper.startsWith("SYST")) {
myOut("215 UNIX Type: L8.");
} else {
myOut("502 Command not implemented.");
}
return false;
}
private void localLog(String s) {
}
private void retr(final String fileName, Socket soc)
throws UnknownHostException, IOException, InterruptedException {
final OutputStream os = soc.getOutputStream();
final byte[] data = connexion.getData(fileName);
if (data != null) {
os.write(data);
}
os.flush();
os.close();
soc.close();
myOut("226 Transfer complete.");
}
private void retrPassif(final String s) throws UnknownHostException, IOException, InterruptedException {
String fileName = s.substring("STOR ".length());
fileName = removeStartingsSlash(fileName);
if (connexion.willExist(fileName) == false) {
myOut("550 No such file.");
return;
}
myOut("150 Opening");
waitForMe(fileName);
final ServerSocket ss = new ServerSocket(port);
final Socket incoming = ss.accept();
retr(fileName, incoming);
ss.close();
}
private void waitForMe(String fileName) throws InterruptedException {
do {
if (connexion.doesExist(fileName)) {
return;
}
Thread.sleep(200L);
} while (true);
}
private void retrActif(final String s) throws UnknownHostException, IOException, InterruptedException {
String fileName = s.substring("STOR ".length());
fileName = removeStartingsSlash(fileName);
if (connexion.willExist(fileName) == false) {
myOut("550 No such file.");
return;
}
myOut("150 Opening");
waitForMe(fileName);
final Socket soc = new Socket(ipClient, port);
retr(fileName, soc);
}
private void storActif(final String s) throws IOException {
final String fileName = removeStartingsSlash(s.substring("STOR ".length()));
myOut("150 FILE: " + fileName);
final Socket soc = new Socket(ipClient, port);
stor(fileName, soc);
}
private void storPassif(final String s) throws IOException {
final String fileName = removeStartingsSlash(s.substring("STOR ".length()));
myOut("150 FILE: " + fileName);
final ServerSocket ss = new ServerSocket(port);
final Socket incoming = ss.accept();
stor(fileName, incoming);
ss.close();
}
private String removeStartingsSlash(String fileName) {
while (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private void stor(String fileName, Socket socket) throws UnknownHostException, IOException {
final InputStream is = socket.getInputStream();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileUtils.copyToStream(is, baos);
myOut("226 Transfer complete.");
if ("png".equalsIgnoreCase(fileName)) {
connexion.setFileFormat(FileFormat.PNG);
} else if ("svg".equalsIgnoreCase(fileName)) {
connexion.setFileFormat(FileFormat.SVG);
} else if ("eps".equalsIgnoreCase(fileName)) {
connexion.setFileFormat(FileFormat.EPS);
}
if (fileName.length() > 3) {
final String data = new String(baos.toByteArray(), ftpServer.getCharset());
final String pngFileName = connexion.getFutureFileName(fileName);
connexion.futureOutgoing(pngFileName);
connexion.addIncoming(fileName, data);
ftpServer.processImage(connexion, fileName);
}
}
private void listActif() throws UnknownHostException, IOException {
myOut("150 Opening ASCII mode data");
final Socket soc = new Socket(ipClient, port);
list(soc);
}
private void listPassif() throws UnknownHostException, IOException {
myOut("150 Opening ASCII mode data");
final ServerSocket ss = new ServerSocket(port);
final Socket incoming = ss.accept();
list(incoming);
ss.close();
}
private void list(final Socket soc) throws IOException {
final PrintWriter listing = SecurityUtils.createPrintWriter(soc.getOutputStream(), true);
final Collection files = connexion.getFiles();
if (files.size() > 0) {
int total = 0;
for (String n : files) {
total += (connexion.getSize(n) + 511) / 512;
}
listing.println("total " + total);
// localLog(total);
for (String n : files) {
final String ls = String.format("%10s %4d %-8s %-8s %8d %3s %2s %5s %s", "-rw-rw-r--", 1, "plantuml",
"plantuml", connexion.getSize(n), "Sep", 28, 2006, n);
listing.println(ls);
// localLog(ls);
}
}
listing.flush();
listing.close();
soc.close();
myOut("226 Listing completed.");
}
private void myOut(String s) {
if (s.indexOf('\t') != -1) {
throw new IllegalArgumentException();
}
pw.println(s);
pw.flush();
}
public void run() {
try {
runInternal();
} catch (Throwable t) {
Logme.error(t);
}
}
}