/*	$Id: ModemServer.h 1147 2013-02-19 17:55:54Z faxguy $ */
/*
 * Copyright (c) 1990-1996 Sam Leffler
 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
 * HylaFAX is a trademark of Silicon Graphics
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */
#ifndef _ModemServer_
#define	_ModemServer_
/*
 * Modem and Protocol Server.
 */
#include <stdarg.h>

#include "ClassModem.h"
#include "FaxRequest.h"
#include "IOHandler.h"
#include "ServerConfig.h"
#include "Timeout.h"

#include <sys/time.h>

#ifdef tcsetattr
#undef tcsetattr			// workaround for SCO
#endif
#ifdef tcgetattr
#undef tcgetattr			// workaround for SCO
#endif

class FaxMachineInfo;
class FaxMachineLog;

/*
 * This class defines the ``server process'' that manages
 * a modem and implements the necessary protocol above the
 * ClassModem driver interface.
 */
class ModemServer : public ServerConfig, public IOHandler {
protected:
    enum Parity {
	NONE            = 0,
	EVEN            = 1,
	ODD             = 2
    };
private:
    FILE*	statusFile;		// server status file
    bool	deduceComplain;		// if true, complain when can't deduce
    bool	changePriority;		// change process priority by state
    bool	noFailBusy;		// don't increment dials counters if busy
    bool	delayConfig;		// suppress effects while reading config
    fxStr	dialRulesFile;		// dial string rules filename
    fxStr	commid;			// communication ID
// generic modem-related stuff
    int		modemFd;		// open modem file
    fxStr	modemDevice;		// name of device to open
    fxStr	modemDevID;		// device identifier
    ClassModem*	modem;			// modem driver
    Timeout	timer;			// timeout support class
    bool	timeout;		// timeout during i/o operations
    bool	modemerror;		// modem error during i/o operations
    fxStr	configFile;		// pathname to configuration file
    BaudRate	curRate;		// current termio baud rate
    Parity	curParity;		// current termio parity setting
    u_int	curVMin;		// current termio VMIN setting
    u_int	curVTime;		// current termio VTIME setting
    u_int	setupAttempts;		// consec. failed attempts to init modem
// SSL Fax stuff
    int		sslFaxFd;		// listening socket
    bool	sslFaxConnection;	// whether or not we got an SSL Fax connection
// buffered i/o stuff
    short	rcvCC;			// # bytes pending in rcvBuf
    short	rcvNext;		// next available byte in rcvBuf
    u_short	rcvBit;			// pending bit of rcvNext to send
    int		gotByte;		// byte held for bit destruction
    bool	sawBlockEnd;		// whether DLE+ETX has been seen
    bool	inputBuffered;		// whether or not modem input is buffered

/*
 * Since the very first HylaFAX (before it was even named such) rcvBuf
 * was always 1024 bytes.  However, since we usually read from modemFd without 
 * O_NONBLOCK, attempting to read 1024 bytes from modemFd would introduce
 * small (~100ms) delays as the read operation would not return as long
 * as the device's write operation was occuring and the 1024 byte buffer
 * was not yet filled.  These delays can really impact fax operations because
 * they are longer than most fax-spec tolerances.  So, we now only use
 * rcvBuf of 1 byte - and that resolves the short read blocking issue.  The
 * downside is that the ModemIO tracing is "wordy" with this (using one 
 * whole line of logging per-byte).  The other upside, though, is that the
 * timestamps on the session logging are accurate to the millisecond.
 */
    u_char	rcvBuf[1];		// receive buffering

    friend class ClassModem;

// configuration stuff
    void	setDialRules(const char* name);
    void	setModemSpeakerVolume(SpeakerVolume);
// state-related stuff
    static const char* stateNames[9];
    static const char* stateStatus[9];

    bool	openDevice(const char* dev);
    bool	reopenDevice();
    bool	tcsetattr(int op, struct termios& term);
    bool	tcgetattr(const char* method, struct termios& term);
    static void setFlow(struct termios&, FlowControl iflow, FlowControl oflow);
    static void setParity(struct termios&, Parity);

// general trace interface
    void	traceModemIO(const char* dir, const u_char* buf, u_int cc);
protected:
    enum ModemServerState {
	BASE		= 0,
	RUNNING		= 1,
	MODEMWAIT	= 2,
	LOCKWAIT	= 3,
	GETTYWAIT	= 4,
	SENDING		= 5,
	ANSWERING	= 6,
	RECEIVING	= 7,
	LISTENING	= 8
    };
    ModemServerState state;
    bool	abortCall;		// abort current send/receive
// logging and tracing
    FaxMachineLog* log;			// current log device

    ModemServer(const fxStr& deviceName, const fxStr& devID);

// modem driver setup+teardown interfaces
    virtual bool setupModem(bool isSend = true);
    virtual bool readyModem();
    virtual void discardModem(bool dropDTR);
    virtual ClassModem* deduceModem(bool isSend = true);
// low-level modem interfaces
    int		getModemFd();				// XXX
    BaudRate	getModemRate() const;
    virtual fxStr getModemCapabilities() const;
// low-level SSL Fax interface
    void	setSSLFaxFd(int fd);
    int		getSSLFaxFd();
    bool	getSSLFaxConnection();
// modem i/o support
    void	timerExpired(long, long);
    void	sendDLEETX();
    int		getModemLine(char buf[], u_int bufSize, long ms = 0);
    int		getModemChar(long ms = 0, bool isquery = false);
    int		getModemBit(long ms = 0);
    int		getLastByte();
    bool	didBlockEnd();
    void	setBlockEnd();
    void	resetBlock();
    bool	isModemInput() const;
    bool	isBusyNoFail() const;
    void	flushModemInput();
    bool	putModem(const void* data, int n, long ms = 0, bool checkread = false);
    bool	putModem1(const void* data, int n, long ms = 0, bool checkread = false);
    void	startTimeout(long ms);
    void	stopTimeout(const char* whichdir);
// modem line control
    bool	sendBreak(bool pause);
    bool	setBaudRate(BaudRate rate);
    bool	setBaudRate(BaudRate rate, FlowControl i, FlowControl o);
    bool	setParity(Parity parity);
    bool	setXONXOFF(FlowControl i, FlowControl o, SetAction act);
    bool	setDTR(bool on);
    bool	setInputBuffering(bool on);
    bool	modemStopOutput();
// modem driver interfaces
    bool	modemWaitForRings(u_short rings, CallType&, CallID&);
    CallType	modemAnswerCall(AnswerType, fxStr&, const char* dialnumber = NULL, bool doSecondAnswer = false);
    void	modemAnswerCallCmd(CallType);
    void	modemFlushInput(bool silent = false);
    void	modemHangup();
// server state and related control interfaces
    void	changeState(ModemServerState, long timeout = 0, const char* msg = NULL);
    void	setServerStatus(const char* fmt, ...);
    void	setProcessPriority(ModemServerState s);
// system logging interfaces
    void	traceServer(const char* fmt ...);
    void	traceProtocol(const char* fmt ...);
    void	traceModemOp(const char* fmt ...);
    void	traceStatus(int kind, const char* fmt ...);
// modem locking interfaces implemented in derived class
    virtual bool canLockModem() = 0;
    virtual bool lockModem() = 0;
    virtual void unlockModem() = 0;
// notification interfaces implemented in derived class
    virtual void notifyModemReady() = 0;
    virtual void notifyModemWedged() = 0;
// configuration stuff
    virtual void readConfig(const fxStr& filename);
    virtual void resetConfig();
    virtual void vconfigError(const char* fmt, va_list ap);
    virtual void vconfigTrace(const char* fmt, va_list ap);
    virtual void vdialrulesTrace(const char* fmt, va_list ap);
    const fxStr& getConfigFile() const;
// call/receive session start+end for controlling logging
    void	beginSession(const fxStr& number, gid_t uid = 0);
    void	endSession();
// abort send/receive session
    virtual void abortSession();
    bool	abortRequested();
public:
    const char* readyStateMsg;

    virtual ~ModemServer();

    virtual void open();
    virtual void close();
    virtual void initialize(int argc, char** argv);

    ClassModem*	getModem();
    const fxStr& getModemDevice() const;
    const fxStr& getModemDeviceID() const;
    const fxStr& getModemNumber() const;
    const fxStr& getCommID() const;

    int		getSessionTracing() const;
    int		getServerTracing() const;
    void	vtraceStatus(int kind, const char* fmt, va_list ap);

    bool	modemReady() const;
    bool	serverBusy() const;
};
inline ClassModem* ModemServer::getModem()		{ return modem; }
inline int ModemServer::getModemFd()			{ return modemFd; }
inline int ModemServer::getSSLFaxFd()			{ return sslFaxFd; }
inline bool ModemServer::getSSLFaxConnection()		{ return sslFaxConnection; }
inline int ModemServer::getServerTracing() const
    { return (tracingLevel); }
inline int ModemServer::getSessionTracing() const
    { return (logTracingLevel); }
inline const fxStr& ModemServer::getModemDevice() const
    { return modemDevice; }
inline const fxStr& ModemServer::getModemDeviceID() const
    { return modemDevID; }
inline const fxStr& ModemServer::getModemNumber() const
    { return FAXNumber; }
inline const fxStr& ModemServer::getCommID() const	{ return commid; }
inline bool ModemServer::isModemInput() const
    { return rcvNext < rcvCC; }
inline bool ModemServer::isBusyNoFail() const
    { return noFailBusy; }

#endif /* _ModemServer_ */
