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