1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.waarp.gateway.ftp.control;
18
19 import java.io.File;
20 import java.io.IOException;
21
22 import io.netty.channel.Channel;
23 import org.waarp.common.command.ReplyCode;
24 import org.waarp.common.command.exception.CommandAbstractException;
25 import org.waarp.common.command.exception.Reply421Exception;
26 import org.waarp.common.command.exception.Reply451Exception;
27 import org.waarp.common.command.exception.Reply502Exception;
28 import org.waarp.common.command.exception.Reply504Exception;
29 import org.waarp.common.database.DbSession;
30 import org.waarp.common.database.data.AbstractDbData.UpdatedInfo;
31 import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
32 import org.waarp.common.future.WaarpFuture;
33 import org.waarp.common.logging.WaarpLogger;
34 import org.waarp.common.logging.WaarpLoggerFactory;
35 import org.waarp.ftp.core.command.AbstractCommand;
36 import org.waarp.ftp.core.command.FtpCommandCode;
37 import org.waarp.ftp.core.command.access.QUIT;
38 import org.waarp.ftp.core.control.BusinessHandler;
39 import org.waarp.ftp.core.data.FtpTransfer;
40 import org.waarp.ftp.core.exception.FtpNoFileException;
41 import org.waarp.ftp.core.file.FtpFile;
42 import org.waarp.ftp.core.session.FtpSession;
43 import org.waarp.ftp.filesystembased.FilesystemBasedFtpRestart;
44 import org.waarp.gateway.ftp.config.AUTHUPDATE;
45 import org.waarp.gateway.ftp.config.FileBasedConfiguration;
46 import org.waarp.gateway.ftp.database.DbConstant;
47 import org.waarp.gateway.ftp.file.FileBasedAuth;
48 import org.waarp.gateway.ftp.file.FileBasedDir;
49 import org.waarp.gateway.kernel.exec.AbstractExecutor;
50 import org.waarp.gateway.kernel.exec.R66PreparedTransferExecutor;
51
52
53
54
55
56
57
58
59 public class ExecBusinessHandler extends BusinessHandler {
60
61
62
63 private static final WaarpLogger logger = WaarpLoggerFactory
64 .getLogger(ExecBusinessHandler.class);
65
66
67
68
69 private DbSession dbFtpSession = null;
70
71
72
73 private DbSession dbR66Session = null;
74 private boolean internalDb = false;
75
76 @Override
77 public void afterTransferDoneBeforeAnswer(FtpTransfer transfer)
78 throws CommandAbstractException {
79
80 if (getFtpSession() == null || getFtpSession().getAuth() == null) {
81 return;
82 }
83 FileBasedAuth auth = (FileBasedAuth) getFtpSession().getAuth();
84 if (auth.isAdmin()) {
85 return;
86 }
87 long specialId = auth.getSpecialId();
88 ReplyCode replyCode = getFtpSession().getReplyCode();
89 logger.debug("Transfer done but action needed: "+(!(replyCode != ReplyCode.REPLY_250_REQUESTED_FILE_ACTION_OKAY && replyCode != ReplyCode.REPLY_226_CLOSING_DATA_CONNECTION)));
90 if (replyCode != ReplyCode.REPLY_250_REQUESTED_FILE_ACTION_OKAY && replyCode != ReplyCode.REPLY_226_CLOSING_DATA_CONNECTION) {
91
92 String message = "Transfer done with code: " + getFtpSession().getReplyCode().getMesg();
93 WaarpActionLogger.logErrorAction(dbFtpSession,
94 specialId, transfer, message, getFtpSession().getReplyCode(), this);
95 return;
96 }
97
98 FtpCommandCode code = transfer.getCommand();
99 logger.debug("Checking action vs auth after transfer: {}", code);
100 switch (code) {
101 case RETR:
102
103 WaarpActionLogger.logAction(dbFtpSession, specialId,
104 "Retrieve executed: OK", this, getFtpSession().getReplyCode(),
105 UpdatedInfo.RUNNING);
106 break;
107 case APPE:
108 case STOR:
109 case STOU:
110
111 WaarpFuture futureCompletion = new WaarpFuture(true);
112 String[] args = new String[6];
113 args[0] = auth.getUser();
114 args[1] = auth.getAccount();
115 args[2] = auth.getBaseDirectory();
116 FtpFile file;
117 try {
118 file = transfer.getFtpFile();
119 } catch (FtpNoFileException e1) {
120
121 String message =
122 "PostExecution in Error for Transfer since No File found: " +
123 transfer.getCommand() + " " +
124 transfer.getStatus() + " " + transfer.getPath();
125 CommandAbstractException exc = new Reply421Exception(
126 "PostExecution in Error for Transfer since No File found");
127 WaarpActionLogger.logErrorAction(dbFtpSession,
128 specialId, transfer, message, exc.code, this);
129 throw exc;
130 }
131 try {
132 args[3] = file.getFile();
133 File newfile = new File(args[2] + args[3]);
134
135
136
137 try {
138 newfile.createNewFile();
139 } catch (IOException e) {
140 throw new Reply421Exception(
141 "PostExecution in Error for Transfer since No File found");
142 } catch (SecurityException e) {
143 throw new Reply421Exception(
144 "PostExecution in Error for Transfer since No File found");
145 }
146
147 if (!newfile.canRead()) {
148
149 String message =
150 "PostExecution in Error for Transfer since File is not readable: " +
151 transfer.getCommand() + " " +
152 newfile.getAbsolutePath() + ":" + newfile.canRead() +
153 " " + transfer.getStatus() + " " + transfer.getPath();
154 CommandAbstractException exc =
155 new Reply421Exception(
156 "Transfer done but force disconnection since an error occurs on PostOperation");
157 WaarpActionLogger.logErrorAction(dbFtpSession,
158 specialId, transfer, message, exc.code, this);
159 throw exc;
160 }
161 } catch (CommandAbstractException e1) {
162
163 String message =
164 "PostExecution in Error for Transfer since No File found: " +
165 transfer.getCommand() + " " +
166 transfer.getStatus() + " " + transfer.getPath();
167 CommandAbstractException exc =
168 new Reply421Exception(
169 "Transfer done but force disconnection since an error occurs on PostOperation");
170 WaarpActionLogger.logErrorAction(dbFtpSession,
171 specialId, transfer, message, exc.code, this);
172 throw exc;
173 }
174 args[4] = transfer.getCommand().toString();
175 args[5] = Long.toString(specialId);
176 AbstractExecutor executor =
177 AbstractExecutor.createAbstractExecutor(auth, args, true, futureCompletion);
178 if (executor instanceof R66PreparedTransferExecutor) {
179 ((R66PreparedTransferExecutor) executor).setDbsession(dbR66Session);
180 }
181 executor.run();
182 try {
183 futureCompletion.await();
184 } catch (InterruptedException e) {
185 }
186 if (futureCompletion.isSuccess()) {
187
188 WaarpActionLogger.logAction(dbFtpSession, specialId,
189 "Post-Command executed: OK", this, getFtpSession().getReplyCode(),
190 UpdatedInfo.RUNNING);
191 } else {
192
193 String message =
194 "PostExecution in Error for Transfer: "
195 +
196 transfer.getCommand()
197 + " "
198 +
199 transfer.getStatus()
200 + " "
201 + transfer.getPath()
202 + "\n "
203 + (futureCompletion.getCause() != null ?
204 futureCompletion.getCause().getMessage()
205 : "Internal error of PostExecution");
206 CommandAbstractException exc =
207 new Reply421Exception(
208 "Transfer done but force disconnection since an error occurs on PostOperation");
209 WaarpActionLogger.logErrorAction(dbFtpSession,
210 specialId, transfer, message, exc.code, this);
211 throw exc;
212 }
213 break;
214 default:
215
216 }
217 }
218
219 @Override
220 public void afterRunCommandKo(CommandAbstractException e) {
221 String message = "ExecHandler: KO: " + getFtpSession() + " " + e.getMessage();
222 long specialId =
223 ((FileBasedAuth) getFtpSession().getAuth()).getSpecialId();
224 WaarpActionLogger.logErrorAction(dbFtpSession,
225 specialId, null, message, e.code, this);
226 ((FileBasedAuth) getFtpSession().getAuth()).setSpecialId(DbConstant.ILLEGALVALUE);
227 }
228
229 @Override
230 public void afterRunCommandOk() throws CommandAbstractException {
231 if (!(this.getFtpSession().getCurrentCommand() instanceof QUIT)
232 && this.dbR66Session != null) {
233 long specialId =
234 ((FileBasedAuth) getFtpSession().getAuth()).getSpecialId();
235 WaarpActionLogger.logAction(dbFtpSession, specialId,
236 "Transfer Command fully executed: OK", this, getFtpSession().getReplyCode(),
237 UpdatedInfo.DONE);
238 ((FileBasedAuth) getFtpSession().getAuth()).setSpecialId(DbConstant.ILLEGALVALUE);
239 }
240 }
241
242 @Override
243 public void beforeRunCommand() throws CommandAbstractException {
244 long specialId = DbConstant.ILLEGALVALUE;
245
246 if (getFtpSession() == null || getFtpSession().getAuth() == null) {
247 return;
248 }
249 FileBasedAuth auth = (FileBasedAuth) getFtpSession().getAuth();
250 if (auth.isAdmin()) {
251 logger.debug("Admin user so all actions are allowed");
252 return;
253 }
254
255 FtpConstraintLimitHandler constraints =
256 ((FileBasedConfiguration) getFtpSession().getConfiguration())
257 .constraintLimitHandler;
258 if (constraints != null) {
259 if (!auth.isIdentified()) {
260
261 } else if (auth.isAdmin()) {
262
263 } else if (!FtpCommandCode.isSpecialCommand(
264 getFtpSession().getCurrentCommand().getCode())) {
265
266 if (constraints.checkConstraintsSleep(1)) {
267 if (constraints.checkConstraints()) {
268
269 logger.info("Server overloaded. {} Try later... \n"
270 + getFtpSession().toString(), constraints.lastAlert);
271 if (FileBasedConfiguration.fileBasedConfiguration.ftpMib != null) {
272 FileBasedConfiguration.fileBasedConfiguration.ftpMib.
273 notifyOverloaded("Server overloaded",
274 getFtpSession().toString());
275 }
276 throw new Reply451Exception("Server overloaded. Try later...");
277 }
278 }
279 }
280 }
281 FtpCommandCode code = getFtpSession().getCurrentCommand().getCode();
282 logger.debug("Checking action vs auth before command: {}", code);
283 switch (code) {
284 case APPE:
285 case STOR:
286 case STOU:
287 auth.setSpecialId(specialId);
288 if (!auth.getCommandExecutor().isValidOperation(true)) {
289 throw new Reply504Exception("STORe like operations are not allowed");
290 }
291
292 specialId = WaarpActionLogger.logCreate(dbFtpSession,
293 "PrepareTransfer: OK",
294 getFtpSession().getCurrentCommand().getArg(),
295 this);
296 auth.setSpecialId(specialId);
297
298 break;
299 case RETR:
300 auth.setSpecialId(specialId);
301 if (!auth.getCommandExecutor().isValidOperation(false)) {
302 throw new Reply504Exception("RETRieve like operations are not allowed");
303 }
304
305 specialId = WaarpActionLogger.logCreate(dbFtpSession,
306 "PrepareTransfer: OK",
307 getFtpSession().getCurrentCommand().getArg(),
308 this);
309 auth.setSpecialId(specialId);
310
311 WaarpFuture futureCompletion = new WaarpFuture(true);
312 String[] args = new String[6];
313 args[0] = auth.getUser();
314 args[1] = auth.getAccount();
315 args[2] = auth.getBaseDirectory();
316 String filename = getFtpSession().getCurrentCommand().getArg();
317 FtpFile file = getFtpSession().getDir().setFile(filename, false);
318 args[3] = file.getFile();
319 args[4] = code.toString();
320 args[5] = Long.toString(specialId);
321 AbstractExecutor executor =
322 AbstractExecutor
323 .createAbstractExecutor(auth, args, false, futureCompletion);
324 if (executor instanceof R66PreparedTransferExecutor) {
325 ((R66PreparedTransferExecutor) executor).setDbsession(dbR66Session);
326 }
327 executor.run();
328 try {
329 futureCompletion.await();
330 } catch (InterruptedException e) {
331 }
332 if (futureCompletion.isSuccess()) {
333
334 if (!file.canRead()) {
335 logger.error("PreExecution in Error for Transfer since " +
336 "File downloaded but not ready to be retrieved: {} " +
337 " {} \n " + (futureCompletion.getCause() != null ?
338 futureCompletion.getCause().getMessage() :
339 "File downloaded but not ready to be retrieved"),
340 args[4], args[3]);
341 throw new Reply421Exception(
342 "File downloaded but not ready to be retrieved");
343 }
344 WaarpActionLogger.logAction(dbFtpSession, specialId,
345 "Pre-Command executed: OK", this, getFtpSession().getReplyCode(),
346 UpdatedInfo.RUNNING);
347 } else {
348
349 logger.error("PreExecution in Error for Transfer since " +
350 "File cannot be prepared to be retrieved: {} " +
351 " {} \n " + (futureCompletion.getCause() != null ?
352 futureCompletion.getCause().getMessage() :
353 "File cannot be prepared to be retrieved"),
354 args[4], args[3]);
355 throw new Reply421Exception(
356 "File cannot be prepared to be retrieved");
357 }
358 break;
359 default:
360
361 }
362 }
363
364 @Override
365 protected void cleanSession() {
366 }
367
368 @Override
369 public void exceptionLocalCaught(Throwable cause) {
370 if (FileBasedConfiguration.fileBasedConfiguration.ftpMib != null) {
371 String mesg;
372 if (cause != null && cause.getMessage() != null) {
373 mesg = cause.getMessage();
374 } else {
375 if (this.getFtpSession() != null) {
376 mesg = "Exception while " + this.getFtpSession().getReplyCode().getMesg();
377 } else {
378 mesg = "Unknown Exception";
379 }
380 }
381 FileBasedConfiguration.fileBasedConfiguration.ftpMib.
382 notifyError("Exception trapped", mesg);
383 }
384 if (FileBasedConfiguration.fileBasedConfiguration.monitoring != null) {
385 if (this.getFtpSession() != null) {
386 FileBasedConfiguration.fileBasedConfiguration.monitoring.
387 updateCodeNoTransfer(this.getFtpSession().getReplyCode());
388 }
389 }
390 }
391
392 @Override
393 public void executeChannelClosed() {
394 if (AbstractExecutor.useDatabase) {
395 if (!internalDb) {
396 if (dbR66Session != null) {
397 dbR66Session.disconnect();
398 dbR66Session = null;
399 }
400 }
401 }
402 if (dbFtpSession != null) {
403 dbFtpSession.disconnect();
404 dbFtpSession = null;
405 }
406 }
407
408 @Override
409 public void executeChannelConnected(Channel channel) {
410 if (AbstractExecutor.useDatabase) {
411 if (org.waarp.openr66.database.DbConstant.admin != null &&
412 org.waarp.openr66.database.DbConstant.admin.isActive()) {
413 try {
414 dbR66Session = new DbSession(org.waarp.openr66.database.DbConstant.admin, false);
415 } catch (WaarpDatabaseNoConnectionException e1) {
416 logger.warn("Database not ready due to {}", e1.getMessage());
417 QUIT command = (QUIT)
418 FtpCommandCode.getFromLine(getFtpSession(), FtpCommandCode.QUIT.name());
419 this.getFtpSession().setNextCommand(command);
420 dbR66Session = null;
421 internalDb = true;
422 }
423 }
424 }
425 if (DbConstant.gatewayAdmin != null && DbConstant.gatewayAdmin.isActive()) {
426 try {
427 dbFtpSession = new DbSession(DbConstant.gatewayAdmin, false);
428 } catch (WaarpDatabaseNoConnectionException e1) {
429 logger.warn("Database not ready due to {}", e1.getMessage());
430 QUIT command = (QUIT)
431 FtpCommandCode.getFromLine(getFtpSession(), FtpCommandCode.QUIT.name());
432 this.getFtpSession().setNextCommand(command);
433 dbFtpSession = null;
434 }
435 }
436 }
437
438 @Override
439 public FileBasedAuth getBusinessNewAuth() {
440 return new FileBasedAuth(getFtpSession());
441 }
442
443 @Override
444 public FileBasedDir getBusinessNewDir() {
445 return new FileBasedDir(getFtpSession());
446 }
447
448 @Override
449 public FilesystemBasedFtpRestart getBusinessNewRestart() {
450 return new FilesystemBasedFtpRestart(getFtpSession());
451 }
452
453 @Override
454 public String getHelpMessage(String arg) {
455 return "This FTP server is only intend as a Gateway. RETRieve actions may be unallowed.\n"
456 + "This FTP server refers to RFC 959, 775, 2389, 2428, 3659 and supports XCRC, XMD5 and XSHA1 commands.\n"
457 + "XCRC, XMD5 and XSHA1 take a simple filename as argument and return \"250 digest-value is the digest of filename\".";
458 }
459
460 @Override
461 public String getFeatMessage() {
462 StringBuilder builder = new StringBuilder("Extensions supported:").append('\n').append(getDefaultFeatMessage());
463 if (getFtpSession().getConfiguration().getFtpInternalConfiguration().isAcceptAuthProt()) {
464 builder.append('\n').append(getSslFeatMessage());
465 }
466 builder.append('\n').append(FtpCommandCode.SITE.name()).append(' ').append("AUTHUPDATE").append("\nEnd");
467 return builder.toString();
468 }
469
470 @Override
471 public String getOptsMessage(String[] args) throws CommandAbstractException {
472 if (args.length > 0) {
473 if (args[0].equalsIgnoreCase(FtpCommandCode.MLST.name()) ||
474 args[0].equalsIgnoreCase(FtpCommandCode.MLSD.name())) {
475 return getMLSxOptsMessage(args);
476 }
477 throw new Reply502Exception("OPTS not implemented for " + args[0]);
478 }
479 throw new Reply502Exception("OPTS not implemented");
480 }
481
482 @Override
483 public AbstractCommand getSpecializedSiteCommand(FtpSession session,
484 String line) {
485 if (getFtpSession() == null || getFtpSession().getAuth() == null) {
486 return null;
487 }
488 if (!session.getAuth().isAdmin()) {
489 return null;
490 }
491 String newline = line;
492 if (newline == null) {
493 return null;
494 }
495 String command = null;
496 String arg = null;
497 if (newline.indexOf(' ') == -1) {
498 command = newline;
499 arg = null;
500 } else {
501 command = newline.substring(0, newline.indexOf(' '));
502 arg = newline.substring(newline.indexOf(' ') + 1);
503 if (arg.length() == 0) {
504 arg = null;
505 }
506 }
507 String COMMAND = command.toUpperCase();
508 if (!COMMAND.equals("AUTHUPDATE")) {
509 return null;
510 }
511 AbstractCommand abstractCommand = new AUTHUPDATE();
512 abstractCommand.setArgs(session, COMMAND, arg, FtpCommandCode.SITE);
513 return abstractCommand;
514 }
515 }