View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.protocol.localhandler;
21  
22  import io.netty.channel.Channel;
23  import io.netty.channel.ChannelFuture;
24  import io.netty.channel.ChannelFutureListener;
25  import org.waarp.common.command.exception.Reply421Exception;
26  import org.waarp.common.command.exception.Reply530Exception;
27  import org.waarp.common.database.exception.WaarpDatabaseException;
28  import org.waarp.common.digest.FilesystemBasedDigest;
29  import org.waarp.common.logging.SysErrLogger;
30  import org.waarp.common.logging.WaarpLogger;
31  import org.waarp.common.logging.WaarpLoggerFactory;
32  import org.waarp.openr66.commander.ClientRunner;
33  import org.waarp.openr66.context.ErrorCode;
34  import org.waarp.openr66.context.R66FiniteDualStates;
35  import org.waarp.openr66.context.R66Result;
36  import org.waarp.openr66.context.R66Session;
37  import org.waarp.openr66.context.authentication.R66Auth;
38  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
39  import org.waarp.openr66.database.data.DbHostAuth;
40  import org.waarp.openr66.database.data.DbTaskRunner;
41  import org.waarp.openr66.protocol.configuration.Configuration;
42  import org.waarp.openr66.protocol.configuration.Messages;
43  import org.waarp.openr66.protocol.exception.OpenR66Exception;
44  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessCancelException;
45  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessException;
46  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessNoWriteBackException;
47  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryAlreadyFinishedException;
48  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryStillRunningException;
49  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessRemoteFileNotFoundException;
50  import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessStopException;
51  import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotAuthenticatedException;
52  import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
53  import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
54  import org.waarp.openr66.protocol.localhandler.packet.AuthentPacket;
55  import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket;
56  import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket;
57  import org.waarp.openr66.protocol.localhandler.packet.StartupPacket;
58  import org.waarp.openr66.protocol.networkhandler.NetworkServerHandler;
59  import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
60  import org.waarp.openr66.protocol.utils.ChannelCloseTimer;
61  import org.waarp.openr66.protocol.utils.ChannelUtils;
62  import org.waarp.openr66.protocol.utils.R66Future;
63  
64  import java.net.InetAddress;
65  import java.net.InetSocketAddress;
66  import java.net.UnknownHostException;
67  
68  import static org.waarp.openr66.context.R66FiniteDualStates.*;
69  
70  /**
71   * Class to implement actions related to general connection handler:
72   * channelClosed, startup, authentication,
73   * and error. Used to store and retrieve the session information.
74   */
75  public abstract class ConnectionActions {
76    /**
77     * Internal Logger
78     */
79    private static final WaarpLogger logger =
80        WaarpLoggerFactory.getLogger(ConnectionActions.class);
81  
82    /**
83     * Session
84     */
85    protected R66Session session;
86    /**
87     * Local Channel Reference
88     */
89    protected LocalChannelReference localChannelReference;
90    /**
91     * Global Digest in receive
92     */
93    protected FilesystemBasedDigest globalDigest;
94    /**
95     * Global Digest in receive using local hash if necessary
96     */
97    protected FilesystemBasedDigest localDigest;
98  
99    final void businessError() {
100     if (session.getBusinessObject() != null) {
101       session.getBusinessObject().checkAtError(session);
102     }
103   }
104 
105   /**
106    * @return the session
107    */
108   public final R66Session getSession() {
109     return session;
110   }
111 
112   /**
113    * @return the localChannelReference
114    */
115   public final LocalChannelReference getLocalChannelReference() {
116     return localChannelReference;
117   }
118 
119   /**
120    * Operations to ensure that channel closing is done correctly
121    */
122   public final void channelClosed() {
123     if (session.getState() == CLOSEDCHANNEL) {
124       return;
125     }
126     final DbTaskRunner runner = session.getRunner();
127     try {
128       if (logger.isDebugEnabled()) {
129         logger.debug("Local Server Channel Closed: {} {}",
130                      localChannelReference != null? localChannelReference :
131                          "no LocalChannelReference",
132                      runner != null? runner.toShortString() : "no runner");
133       }
134       // clean session objects like files
135       boolean mustFinalize = true;
136       final R66Future transfer = localChannelReference != null?
137           localChannelReference.getFutureRequest() : null;
138       if (transfer != null && transfer.isDone()) {
139         // already done
140         mustFinalize = false;
141       } else {
142         if (localChannelReference != null) {
143           final R66Future fvr = localChannelReference.getFutureValidRequest();
144           fvr.awaitOrInterruptible(Configuration.configuration.getTimeoutCon());
145           if (fvr.isDone()) {
146             if (!fvr.isSuccess()) {
147               // test if remote server was Overloaded
148               if (fvr.getResult() != null &&
149                   fvr.getResult().getCode() == ErrorCode.ServerOverloaded) {
150                 // ignore
151                 mustFinalize = false;
152               }
153             } else {
154               mustFinalize = false;
155             }
156           }
157           logger.debug("Must Finalize: {}", mustFinalize);
158           if (mustFinalize) {
159             session.newState(ERROR);
160             final R66Result finalValue = new R66Result(
161                 new OpenR66ProtocolSystemException(
162                     runner != null? Messages.getString("LocalServerHandler.4") :
163                         Messages.getString("HttpSslHandler.37")),
164                 //$NON-NLS-1$
165                 session, true, ErrorCode.FinalOp, runner); // True since closed
166             try {
167               tryFinalizeRequest(finalValue);
168             } catch (final OpenR66Exception ignored) {
169               // ignore
170             }
171           }
172         }
173       }
174       if (mustFinalize && runner != null && runner.isRequestOnRequested() &&
175           localChannelReference != null) {
176         // Since requested : log
177         final R66Result result = transfer.getResult();
178         if (transfer.isDone() && transfer.isSuccess()) {
179           logger.info("TRANSFER REQUESTED RESULT:     SUCCESS     {}",
180                       (result != null? result : "no result"));
181         } else {
182           logger.error("TRANSFER REQUESTED RESULT:     FAILURE     " +
183                        (result != null? result.toString() : "no result"));
184         }
185       }
186       session.setStatus(50);
187       session.newState(CLOSEDCHANNEL);
188       session.partialClear();
189       session.setStatus(51);
190       if (localChannelReference != null) {
191         if (localChannelReference.getDbSession() != null) {
192           localChannelReference.getDbSession().endUseConnection();
193           logger.debug("End Use Connection");
194         }
195         NetworkTransaction.checkClosingNetworkChannel(
196             localChannelReference.getNetworkChannelObject(),
197             localChannelReference);
198         session.setStatus(52);
199       } else {
200         logger.debug(
201             "Local Server Channel Closed but no LocalChannelReference");
202       }
203       // Now if runner is not yet finished, finish it by force
204       if (mustFinalize && localChannelReference != null && !transfer.isDone()) {
205         final R66Result finalValue = new R66Result(
206             new OpenR66ProtocolSystemException(
207                 Messages.getString("LocalServerHandler.11")),
208             //$NON-NLS-1$
209             session, true, ErrorCode.FinalOp, runner);
210         localChannelReference.invalidateRequest(finalValue);
211         // In case stop the attached thread if any
212         final ClientRunner clientRunner =
213             localChannelReference.getClientRunner();
214         if (clientRunner != null) {
215           try {
216             Thread.sleep(Configuration.RETRYINMS);
217           } catch (final InterruptedException e1) {//NOSONAR
218             SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
219           }
220           clientRunner.interrupt();
221         }
222       }
223     } finally {
224       if (runner != null) {
225         runner.clean();
226       }
227       final LocalTransaction lt =
228           Configuration.configuration.getLocalTransaction();
229       if (lt != null && localChannelReference != null) {
230         localChannelReference.close();
231       }
232     }
233   }
234 
235   /**
236    * Create a new Session at startup of the channel
237    */
238   public final void newSession() {
239     session = new R66Session();
240     session.setStatus(60);
241     session.setBusinessObject(
242         Configuration.configuration.getR66BusinessFactory()
243                                    .getBusinessInterface(session));
244   }
245 
246   protected final void setLocalChannelReference(
247       final LocalChannelReference localChannelReference) {
248     if (localChannelReference != null) {
249       this.localChannelReference = localChannelReference;
250       if (session != null) {
251         session.setLocalChannelReference(localChannelReference);
252       }
253     }
254   }
255 
256   /**
257    * Startup of the session and the local channel reference
258    *
259    * @param packet
260    *
261    * @throws OpenR66ProtocolPacketException
262    */
263   public final void startup(final StartupPacket packet)
264       throws OpenR66ProtocolPacketException {
265     packet.clear();
266     if (localChannelReference == null) {
267       session.newState(ERROR);
268       logger.error(Messages.getString("LocalServerHandler.1")); //$NON-NLS-1$
269       session.setStatus(40);
270       throw new OpenR66ProtocolPacketException("Cannot startup connection");
271     }
272     if (session.getBusinessObject() != null) {
273       try {
274         session.getBusinessObject().checkAtConnection(session);
275       } catch (final OpenR66RunnerErrorException e) {
276         final ErrorPacket error =
277             new ErrorPacket("Connection refused by business logic",
278                             ErrorCode.ConnectionImpossible.getCode(),
279                             ErrorPacket.FORWARDCLOSECODE);
280         ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
281                                               false);
282         session.setStatus(40);
283         return;
284       }
285     }
286     session.newState(STARTUP);
287     localChannelReference.validateStartup(true);
288     session.setStatus(41);
289     logger.debug("Startup done for {}", localChannelReference.getLocalId());
290   }
291 
292   /**
293    * Refuse a connection
294    *
295    * @param packet
296    * @param e1
297    *
298    * @throws OpenR66ProtocolPacketException
299    */
300   private void refusedConnection(final AuthentPacket packet, Exception e1)
301       throws OpenR66ProtocolPacketException {
302     logger.error(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$
303                  localChannelReference.getNetworkChannel().remoteAddress() +
304                  " : " + packet.getHostId());
305     if (logger.isDebugEnabled()) {
306       logger.debug(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$
307                    localChannelReference.getNetworkChannel().remoteAddress() +
308                    " : " + packet.getHostId(), e1);
309     }
310     if (Configuration.configuration.getR66Mib() != null) {
311       Configuration.configuration.getR66Mib().notifyError(
312           "Connection not allowed from " +
313           localChannelReference.getNetworkChannel().remoteAddress() +
314           " since " + e1.getMessage(), packet.getHostId());
315     }
316 
317     DbHostAuth auth = null;
318     try {
319       auth = new DbHostAuth(packet.getHostId());
320     } catch (final WaarpDatabaseException e) {
321       logger.warn(
322           "Cannot find the authentication " + packet.getHostId() + " : {}",
323           e.getMessage());
324     }
325     if (auth != null && !auth.isActive()) {
326       e1 = new Reply530Exception(
327           "Host is Inactive therefore connection is refused");
328     }
329     final R66Result result = new R66Result(new OpenR66ProtocolSystemException(
330         Messages.getString("LocalServerHandler.6") +
331         //$NON-NLS-1$
332         localChannelReference.getNetworkChannel().remoteAddress(), e1), session,
333                                            true, ErrorCode.BadAuthent, null);
334     localChannelReference.invalidateRequest(result);
335     session.newState(ERROR);
336     final ErrorPacket error =
337         new ErrorPacket(e1.getMessage(), ErrorCode.BadAuthent.getCode(),
338                         ErrorPacket.FORWARDCLOSECODE);
339     ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, false);
340     localChannelReference.validateConnection(false, result);
341     final Channel networkchannel = localChannelReference.getNetworkChannel();
342     final boolean valid =
343         NetworkTransaction.shuttingDownNetworkChannelBlackList(
344             localChannelReference.getNetworkChannelObject());
345     logger.warn(
346         "Closing and blacklisting NetworkChannelReference since LocalChannel is not authenticated: " +
347         valid);
348     ChannelCloseTimer.closeFutureTransaction(this);
349     ChannelCloseTimer.closeFutureChannel(networkchannel);
350     businessError();
351   }
352 
353   /**
354    * Authentication
355    *
356    * @param packet
357    *
358    * @throws OpenR66ProtocolPacketException
359    */
360   public final void authent(final AuthentPacket packet, final boolean isSsl)
361       throws OpenR66ProtocolPacketException {
362     logger.debug("AUTHENT {}", packet);
363     if (packet.isToValidate()) {
364       if (session.getState() != AUTHENTD) {
365         session.newState(AUTHENTR);
366       }
367     }
368 
369     logger.debug("LocalChannelReference null? {}",
370                  localChannelReference == null);
371     if (localChannelReference.getDbSession() != null) {
372       localChannelReference.getDbSession().useConnection();
373     }
374     if (localChannelReference.getNetworkChannelObject() != null) {
375       localChannelReference.getNetworkChannelObject()
376                            .setHostId(packet.getHostId());
377     } else {
378       session.newState(ERROR);
379       logger.error("Service unavailable: " + packet.getHostId());
380       final R66Result result = new R66Result(
381           new OpenR66ProtocolSystemException("Service unavailable"), session,
382           true, ErrorCode.ConnectionImpossible, null);
383       localChannelReference.invalidateRequest(result);
384       final ErrorPacket error = new ErrorPacket("Service unavailable",
385                                                 ErrorCode.ConnectionImpossible.getCode(),
386                                                 ErrorPacket.FORWARDCLOSECODE);
387       ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
388                                             false);
389       localChannelReference.validateConnection(false, result);
390       ChannelCloseTimer.closeFutureTransaction(this);
391       session.setStatus(43);
392       businessError();
393       return;
394     }
395     try {
396       session.getAuth().connection(packet.getHostId(), packet.getKey(), isSsl);
397     } catch (final Reply530Exception e1) {
398       refusedConnection(packet, e1);
399       session.setStatus(42);
400       return;
401     } catch (final Reply421Exception e1) {
402       session.newState(ERROR);
403       logger.error("Service unavailable: " + packet.getHostId() + ": {}",
404                    e1.getMessage());
405       final R66Result result = new R66Result(
406           new OpenR66ProtocolSystemException("Service unavailable", e1),
407           session, true, ErrorCode.ConnectionImpossible, null);
408       localChannelReference.invalidateRequest(result);
409       final ErrorPacket error = new ErrorPacket("Service unavailable",
410                                                 ErrorCode.ConnectionImpossible.getCode(),
411                                                 ErrorPacket.FORWARDCLOSECODE);
412       ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
413                                             false);
414       localChannelReference.validateConnection(false, result);
415       ChannelCloseTimer.closeFutureTransaction(this);
416       session.setStatus(43);
417       businessError();
418       return;
419     }
420     localChannelReference.setPartner(packet.getHostId());
421     // Now if configuration say to do so: check remote ip address
422     if (Configuration.configuration.isCheckRemoteAddress() &&
423         !localChannelReference.getPartner().isProxified()) {
424       final DbHostAuth host = R66Auth.getServerAuth(packet.getHostId());
425       boolean toTest = false;
426       assert host != null;
427       if (!host.isProxified()) {
428         if (host.isClient()) {
429           if (Configuration.configuration.isCheckClientAddress()) {
430             // 0.0.0.0 so nothing
431             toTest = !host.isNoAddress();
432           }
433         } else {
434           toTest = true;
435         }
436       }
437       if (toTest) {
438         // Real address so compare
439         final String address = host.getAddress();
440         InetAddress[] inetAddress;
441         try {
442           inetAddress = InetAddress.getAllByName(address);
443         } catch (final UnknownHostException e) {
444           inetAddress = null;
445         }
446         if (inetAddress != null) {
447           final InetSocketAddress socketAddress =
448               (InetSocketAddress) session.getRemoteAddress();
449           boolean found = false;
450           for (final InetAddress inetAddres : inetAddress) {
451             if (socketAddress.getAddress().equals(inetAddres)) {
452               found = true;
453               break;
454             }
455           }
456           if (!found) {
457             // error
458             refusedConnection(packet,
459                               new OpenR66ProtocolNotAuthenticatedException(
460                                   "Server IP not authenticated: " +
461                                   inetAddress[0] + " compare to " +
462                                   socketAddress.getAddress()));
463             session.setStatus(104);
464             return;
465           }
466         }
467       }
468     }
469     if (session.getBusinessObject() != null) {
470       try {
471         session.getBusinessObject().checkAtAuthentication(session);
472       } catch (final OpenR66RunnerErrorException e) {
473         refusedConnection(packet, new OpenR66ProtocolNotAuthenticatedException(
474             e.getMessage()));
475         session.setStatus(104);
476         return;
477       }
478     }
479     // Check compression
480     session.setCompressionEnabled(
481         localChannelReference.getPartner().isCompression() &&
482         Configuration.configuration.isCompressionAvailable());
483     final R66Result result =
484         new R66Result(session, true, ErrorCode.InitOk, null);
485     if (session.getState() != AUTHENTD) {
486       session.newState(AUTHENTD);
487     }
488     localChannelReference.validateConnection(true, result);
489     logger.debug("Local Server Channel Validated: {} ",
490                  localChannelReference != null? localChannelReference :
491                      "no LocalChannelReference");
492     session.setStatus(44);
493     NetworkTransaction.addClient(
494         localChannelReference.getNetworkChannelObject(), packet.getHostId());
495     if (packet.isToValidate()) {
496       // only requested
497       packet.validate(session.getAuth().isSsl());
498       ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
499                                             false);
500       session.setStatus(98);
501     }
502     // Checking if partner is able to reuse authentication
503     if (allowReusableAuthentication() &&
504         Configuration.configuration.getVersions()
505                                    .containsKey(session.getAuth().getUser()) &&
506         Configuration.configuration.getVersions()
507                                    .get(session.getAuth().getUser())
508                                    .supportReuseAuthentication()) {
509       localChannelReference.getNetworkChannel()
510                            .attr(NetworkServerHandler.REUSABLE_AUTH_KEY)
511                            .set(session.getAuth().clone());
512     }
513     logger.debug("Partner: {} from {}", localChannelReference.getPartner(),
514                  Configuration.configuration.getVersions());
515   }
516 
517   public final boolean allowReusableAuthentication() {
518     return !Configuration.configuration.isAuthentNoReuse();
519   }
520 
521   public final boolean hasReusableAuthentication() {
522     return allowReusableAuthentication() &&
523            localChannelReference.getNetworkChannel().hasAttr(
524                NetworkServerHandler.REUSABLE_AUTH_KEY) &&
525            localChannelReference.getNetworkChannel()
526                                 .attr(NetworkServerHandler.REUSABLE_AUTH_KEY)
527                                 .get() != null;
528   }
529 
530   public final boolean validateAuthenticationReuse()
531       throws OpenR66ProtocolNotAuthenticatedException {
532     if (hasReusableAuthentication()) {
533       // Already authenticated
534       final R66Auth source = localChannelReference.getNetworkChannel().attr(
535           NetworkServerHandler.REUSABLE_AUTH_KEY).get();
536       if (source != null) {
537         if (localChannelReference.getDbSession() != null) {
538           localChannelReference.getDbSession().useConnection();
539         }
540         if (localChannelReference.getNetworkChannelObject() != null) {
541           localChannelReference.getNetworkChannelObject()
542                                .setHostId(source.getUser());
543         }
544         localChannelReference.getSession().getAuth().setFromClone(source);
545         localChannelReference.setPartner(source.getUser());
546         if (session.getBusinessObject() != null) {
547           try {
548             session.getBusinessObject().checkAtAuthentication(session);
549           } catch (final OpenR66RunnerErrorException e) {
550             session.setStatus(104);
551             throw new OpenR66ProtocolNotAuthenticatedException(e.getMessage());
552           }
553         }
554         // Check compression
555         session.setCompressionEnabled(
556             localChannelReference.getPartner().isCompression() &&
557             Configuration.configuration.isCompressionAvailable());
558         final R66Result result =
559             new R66Result(session, true, ErrorCode.InitOk, null);
560         localChannelReference.validateConnection(true, result);
561         session.setStatus(44);
562         NetworkTransaction.addClient(
563             localChannelReference.getNetworkChannelObject(), source.getUser());
564         final R66FiniteDualStates state =
565             localChannelReference.getSessionState();
566         if (state == AUTHENTR || state == STARTUP) {
567           localChannelReference.sessionNewState(AUTHENTD);
568         }
569         logger.debug("Authentication is done using reuse");
570         return true;
571       }
572     }
573     logger.debug("Authentication will be done as usual");
574     return false;
575   }
576 
577   /**
578    * Receive a connection error
579    *
580    * @param packet
581    */
582   public final void connectionError(final ConnectionErrorPacket packet) {
583     // do something according to the error
584     logger.error(localChannelReference.getRequestId() + ": " + packet);
585     ErrorCode code = ErrorCode.ConnectionImpossible;
586     if (packet.getSmiddle() != null) {
587       code = ErrorCode.getFromCode(packet.getSmiddle());
588     }
589     localChannelReference.invalidateRequest(
590         new R66Result(new OpenR66ProtocolSystemException(packet.getSheader()),
591                       session, true, code, null));
592     // True since closing
593     session.newState(ERROR);
594     session.setStatus(45);
595     businessError();
596     localChannelReference.close();
597   }
598 
599   /**
600    * Receive a remote error
601    *
602    * @param packet
603    *
604    * @throws OpenR66RunnerErrorException
605    * @throws OpenR66ProtocolSystemException
606    * @throws OpenR66ProtocolBusinessException
607    */
608   public final void errorMesg(final ErrorPacket packet)
609       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException,
610              OpenR66ProtocolBusinessException {
611     // do something according to the error
612     if (session.getLocalChannelReference().getFutureRequest().isDone()) {
613       // already canceled or successful
614       return;
615     }
616     logger.error(localChannelReference.getRequestId() + ": " + packet);
617     session.setStatus(46);
618     final ErrorCode code = ErrorCode.getFromCode(packet.getSmiddle());
619     session.getLocalChannelReference()
620            .setErrorMessage(packet.getSheader(), code);
621     final OpenR66ProtocolBusinessException exception;
622     if (code.code == ErrorCode.CanceledTransfer.code) {
623       NetworkTransaction.stopRetrieve(session.getLocalChannelReference());
624       logger.info("Stop retrieving file: the transfer has been canceled");
625       exception =
626           new OpenR66ProtocolBusinessCancelException(packet.getSheader());
627       final int rank = 0;
628       final DbTaskRunner runner = session.getRunner();
629       if (runner != null) {
630         runner.setRankAtStartup(rank);
631         runner.stopOrCancelRunner(code);
632       }
633       final R66Result result =
634           new R66Result(exception, session, true, code, runner);
635       // now try to inform other
636       session.setFinalizeTransfer(false, result);
637       try {
638         ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
639                                               false).addListener(
640             new RunnerChannelFutureListener(localChannelReference, result));
641       } catch (final OpenR66ProtocolPacketException ignored) {
642         // ignore
643       }
644       return;
645     } else if (code.code == ErrorCode.StoppedTransfer.code) {
646       NetworkTransaction.stopRetrieve(session.getLocalChannelReference());
647       logger.info("Stop retrieving file: the transfer has been stopped");
648       exception = new OpenR66ProtocolBusinessStopException(packet.getSheader());
649       final String[] vars = packet.getSheader().split(" ");
650       final String var = vars[vars.length - 1];
651       final int rank = Integer.parseInt(var);
652       final DbTaskRunner runner = session.getRunner();
653       if (runner != null) {
654         if (rank < runner.getRank()) {
655           runner.setRankAtStartup(rank);
656         }
657         runner.stopOrCancelRunner(code);
658       }
659       final R66Result result =
660           new R66Result(exception, session, true, code, runner);
661       // now try to inform other
662       session.setFinalizeTransfer(false, result);
663       try {
664         ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
665                                               false).addListener(
666             new RunnerChannelFutureListener(localChannelReference, result));
667       } catch (final OpenR66ProtocolPacketException ignored) {
668         // ignore
669       }
670       return;
671     } else if (code.code == ErrorCode.QueryAlreadyFinished.code) {
672       final DbTaskRunner runner = session.getRunner();
673       if (runner == null) {
674         exception =
675             new OpenR66ProtocolBusinessCancelException(packet.toString());
676       } else {
677         if (session.isSender()) {
678           exception = new OpenR66ProtocolBusinessQueryAlreadyFinishedException(
679               packet.getSheader());
680           runner.finishTransferTask(code);
681           tryFinalizeRequest(
682               new R66Result(exception, session, true, code, runner));
683         } else {
684           exception =
685               new OpenR66ProtocolBusinessCancelException(packet.toString());
686         }
687       }
688       throw exception;
689     } else if (code.code == ErrorCode.QueryStillRunning.code) {
690       exception = new OpenR66ProtocolBusinessQueryStillRunningException(
691           packet.getSheader());
692       throw exception;
693     } else if (code.code == ErrorCode.BadAuthent.code) {
694       exception =
695           new OpenR66ProtocolNotAuthenticatedException(packet.toString());
696     } else if (code.code == ErrorCode.QueryRemotelyUnknown.code) {
697       exception = new OpenR66ProtocolBusinessCancelException(packet.toString());
698     } else if (code.code == ErrorCode.FileNotFound.code) {
699       exception = new OpenR66ProtocolBusinessRemoteFileNotFoundException(
700           packet.toString());
701     } else {
702       exception =
703           new OpenR66ProtocolBusinessNoWriteBackException(packet.toString());
704     }
705     session.setFinalizeTransfer(false,
706                                 new R66Result(exception, session, true, code,
707                                               session.getRunner()));
708     throw exception;
709   }
710 
711   /**
712    * Try to finalize the request if possible
713    *
714    * @param errorValue in case of Error
715    *
716    * @throws OpenR66ProtocolSystemException
717    * @throws OpenR66RunnerErrorException
718    */
719   public final void tryFinalizeRequest(final R66Result errorValue)
720       throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
721     session.tryFinalizeRequest(errorValue);
722   }
723 
724   /**
725    * Class to finalize a runner when the future is over
726    */
727   private static final class RunnerChannelFutureListener
728       implements ChannelFutureListener {
729     private final LocalChannelReference localChannelReference;
730     private final R66Result result;
731 
732     private RunnerChannelFutureListener(
733         final LocalChannelReference localChannelReference,
734         final R66Result result) {
735       this.localChannelReference = localChannelReference;
736       this.result = result;
737     }
738 
739     @Override
740     public final void operationComplete(final ChannelFuture future) {
741       localChannelReference.invalidateRequest(result);
742       ChannelCloseTimer.closeFutureTransaction(
743           localChannelReference.getServerHandler());
744     }
745 
746   }
747 
748 }