/* doscan - Denial Of Service Capable Auditing of Networks       -*- C++ -*-
 * Copyright (C) 2003 Florian Weimer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef HALF_DUPLEX_H
#define HALF_DUPLEX_H

#include "event_queue.h"

#include <string>

class half_duplex_handler : public event_queue::fd_handler {

  // Override routines inherited from fd_handler.

  virtual bool on_timeout(ticks_t);
  virtual bool on_activity(activity);

  bool on_activity_read();
  bool on_activity_write();

  bool invoke_ready();

  int get_error();

protected:

  int state;

  std::string receive_buffer;
  std::string send_buffer;

private:
  std::string::size_type receive_goal;
  std::string::size_type send_offset;

  // invoke_ready() sets this variable to true before calling ready(),
  // and the requested*() and send() members set it to false.  Thus we
  // can check if a new operation has been requested.

  bool stop;

public:
  // Handles an existing half-duplex connection.
  //
  // If first_read is true, the first operation is a read operation,
  // otherwise it's a write operation.

  half_duplex_handler(event_queue&, int fd, bool first_read);

  // Automatically closes the file descriptor.

  virtual ~half_duplex_handler();

  // ready() is called when the requested amount of data has been
  // received, or a send operation has completed and some data has
  // been received.
  //
  // The first state is 0.  Its value is special and the state should
  // not be entered again.

  virtual void ready() = 0;

  // This is called when an error is detected, or the peer closes the
  // connection.  The argument is the errno number, or 0 if the
  // connection has just been closed.

  virtual void error(int) = 0;

  // request_data() should be called by ready() to request more data.
  // If count bytes are received, ready() is invoked again, with the
  // given state.  (If count is zero, all the data in the socket
  // buffer is read.)

  void request_data(unsigned count = 0);

  // request_more_data() is like request_data(), only the data is
  // appended to the existing buffer.

  void request_more_data(unsigned count = 0);

  // send() should be called by ready() to send the data in
  // send_buffer.  After completion, ready() is invoked with the new
  // state.

  void send();

  // Line send(), but assigns the argument to send_buffer.

  void send(const std::string&);
};

inline void
half_duplex_handler::request_more_data(unsigned count)
{
  receive_goal = count;
  watch(watch_read);
  stop = false;
}

inline void
half_duplex_handler::send()
{
  send_offset = 0;
  watch(watch_write);
  stop = false;
}

inline void
half_duplex_handler::send(const std::string& data)
{
  send_buffer.assign(data);
  send();
}

inline bool
half_duplex_handler::invoke_ready()
{
  stop = true;
  ready();
  return !stop;
}

#endif // HALF_DUPLEX_H
