/*
 * kbiffmonitor.h
 * Copyright (C) 1999-2001 Kurt Granroth <granroth@kde.org>
 *
 * $Id$
 *
 */
#ifndef KBIFFMONITOR_H
#define KBIFFMONITOR_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h> // Needed on some systems.
#endif

#include <sys/time.h>

#include <tqobject.h>
#include <tqptrlist.h>
#include <tqfileinfo.h>

#ifdef USE_SSL
#include <kssl.h>
#endif

class KBiffURL;
class TQString;

/**
 * @internal
 * Internal class to store UIDL list
 */
class KBiffUidlList : public TQPtrList<TQString>
{
protected:
    int compareItems(Item s1, Item s2)
    {
        TQString *str1, *str2;
        str1 = (TQString *)s1;
        str2 = (TQString *)s2;
        if((*str1) == (*str2))
            return 0;
        else
            return -1;
    }
};

/**
 * @internal
 */

#define SOCKET_TIMEOUT 5

class KBiffSocket
{
public:
    KBiffSocket();
    virtual ~KBiffSocket();

    bool connectSocket(const TQString& host, unsigned short int port);
    bool active();

    bool isAsync();
    void setAsync(bool on);
    
#ifdef USE_SSL
    bool isSSL();
    void setSSL(bool on);
#endif // USE_SSL

    int numberOfMessages();
    int numberOfNewMessages();

    void close();

protected:
    TQString readLine();
    int writeLine(const TQString& line);

    bool    async;
    
    struct timeval socketTO;

    int socketFD;
    fd_set socketFDS;
    int messages;
    int newMessages;
    TQString banner;   // the first line read from a new connection

#ifdef USE_SSL
    bool usessl;
    KSSL *ssltunnel;
#endif // USE_SSL
};

/**
 * @internal
 */
class KBiffImap : public KBiffSocket
{
public:
    KBiffImap();
    virtual ~KBiffImap();

    bool command(const TQString& line, unsigned int seq);
    TQString mungeUserPass(const TQString& old_user);
    void resetNumbers();
    /**
     * @internal
     *
     * @brief Encapsulates all authentication to the IMAP server
     *
     * All authentication, including the determination of which authentication
     * mechanism to use, is performed in this method.  To add an
     * authentication method, one will need to modify this method,
     * KBiffMonitor::checkImap and KBiffImap::command
     *
     * @param pseq a pointer to the number of the next IMAP command.
     *        This value will be incremented depending on the number of
     *        commands necessary for authentication.
     * @param user the user name required for authentication
     * @param pass the password required for authentication
     */
    bool authenticate(int *pseq, const TQString& user, const TQString& pass);

protected:
    /**
     * @internal
     *
     * @brief does the IMAP server support AUTH=CRAM-MD5 ?
     */
    bool auth_cram_md5;

    /**
     * @internal
     *
     * @brief the CRAM-MD5 challenge (base64 decoded) as issued by the server
     */
    TQString chall_cram_md5;
};

/**
 * @internal
 */
class KBiffPop : public KBiffSocket
{
public:
    KBiffPop();
    virtual ~KBiffPop();

    bool command(const TQString& line);
    KBiffUidlList getUidlList() const;

    void close();
    
    // Parses the banner message in the initial server response
    bool parseBanner(void);
    // Unset to disable APOP authentication
    void setApop( bool enabled );
    /**
     * @internal
     *
     * @brief Encapsulates all authentication to the POP3 server
     *
     * All authentication, including the determination of which authentication
     * mechanism to use, is performed in this method.  To add an
     * authentication method, one will need to modify this method,
     * KBiffMonitor::checkPop and KBiffPop::command
     *
     * @param user the user name required for authentication
     * @param pass the password required for authentication
     */
    bool authenticate(const TQString& user, const TQString& pass);

protected:
    KBiffUidlList  uidlList;    
    /**
     * @internal
     * @brief does the server support APOP authentication ?
     */
    bool auth_apop;
    /**
     * @internal
     * @brief the APOP challenge from the server
     */
    TQCString chall_apop;
    /**
     * @internal
     * @brief does the server support CRAM-MD5 authentication ?
     */
    bool auth_cram_md5;
    /**
     * @internal
     * @brief the CRAM-MD5 challenge (base64 decoded)
     */
    TQString chall_cram_md5;  // the CRAM-MD5 challenge (base64 decoded)
    /**
     * @internal
     * @brief does the user want APOP authentication
     */
    bool use_apop;
};

/**
 * @internal
 */
class KBiffNntp : public KBiffSocket
{
public:
    virtual ~KBiffNntp();

    bool command(const TQString& line);
    int first() const;
    int last() const;
protected:
    int firstMsg;
    int lastMsg;
};

typedef enum
{
    NewMail = 0,
    NoMail,
    OldMail,
    NoConn,
    UnknownState
} KBiffMailState;

/**
 * A "biff"-like class that can monitor local and remote mailboxes for new
 * mail.  KBiffMonitor currently supports eight protocols.
 * 
 * <UL>
 * <LI>mbox</LI> Unix style mailbox files
 * <LI>pop3</LI> POP3 
 * <LI>imap4</LI> imap4 
 * <LI>maildir</LI> Mailboxes in maildir format
 * <LI>mh</LI> Mailboxes in MH format
 * <LI>file</LI> Simple files (no parsing)
 * <LI>nntp</LI> USENET newsgroups
 * <LI>imap4s</LI> imap4 over SSL
 * <LI>pop3s</LI> POP3 over SSL
 * </UL>
 *
 * A typical usage would look like so:
 *
 * <PRE>
 *    KBiffMonitor mon;
 *    mon.setMailbox("imap4://user:password@some.host.net/mailbox");
 *    mon.setPollInterval(15);
 *    mon.start();
 *
 *    connect(&mon, TQ_SIGNAL(signal_newMail()), this, TQ_SLOT(processNewMail()));
 *    connect(&mon, TQ_SIGNAL(signal_oldMail()), this, TQ_SLOT(processOldMail()));
 *    connect(&mon, TQ_SIGNAL(signal_noMail()), this, TQ_SLOT(processNoMail()));
 *    connect(&mon, TQ_SIGNAL(signal_noConn()), this, TQ_SLOT(processNoConn()));
 * </PRE>
 *
 * @short A "biff" class that monitors local and remote mailboxes
 * @author Kurt Granroth <granroth@kde.org>
 * @version $Id$
 */
class KBiffMonitor : public TQObject
{

    TQ_OBJECT
public:

    /**
     * Constructor.  Does not take any arguments
     */
    KBiffMonitor();

    /**
     * Destructor.
     */
    virtual ~KBiffMonitor();

    /**
     * Returns the current state of the mailbox (NewMail, OldMail, NoMail, or
     * UnknownState)
     */
    KBiffMailState getMailState() const { return mailState; }

    /**
     * Returns the simpleURL of current mailbox being monitored
     */
    const TQString getMailbox() const { return simpleURL; }

    /**
     * Sets or Returns the key of current mailbox being monitored
     */
    const TQString getMailboxKey() const { return key; }

    /**
     * Returns the type of mailbox being monitored
     */
    const TQString getProtocol() const { return protocol; }
    /**
     * Returns <CODE>true</CODE> is KBiffMonitor is currently monitoring
     * a mailbox.
     */
    bool isRunning() { return started; }

    /**
     * Returns the number of new messages for the current mailbox
     */
    int newMessages() { return newCount; }

    /**
     * Returns the number of messages for the current mailbox
     */
    int curMessages() { return curCount; }


    void saveConfig();
    void readConfig();

public slots:
    /**
     * Sets the mailbox to monitor.  It uses a KBiffURL to specify the
     * protocol, host, username, password, port and path (depending on
     * protocol type).  KBiffMonitor recognizes eight protocols:
     * 
     * <UL>
     * <LI>mbox</LI> Unix style mailbox files
     * <LI>pop3</LI> POP3 
     * <LI>imap4</LI> IMAP4 
     * <LI>maildir</LI> Mailboxes in maildir format
     * <LI>mh</LI> Mailboxes in MH format
     * <LI>nttp</LI> USENET newsgroups
     * <LI>imap4s</LI> imap4 over SSL
     * <LI>pop3s</LI> POP3 over SSL
     * </UL>
     *
     * Some examples:
     * <PRE>
     * mbox:/var/spool/mail/granroth
     * </PRE>
     *
     * This would monitor a local file called '/var/spool/mail/granroth'
     *
     * <PRE>
     * pop3://granroth:password@host.net:1234
     * </PRE>
     *
     * This would monitor POP3 mailbox 'granroth' on server 'host.net'
     * using 1234 as the port and 'password' as the password.
     *
     * <PRE>
     * imap4://granroth:password@host.net/Mail/mailbox
     * </PRE>
     *
     * This would monitor IMAP4 mailbox 'Mail/mailbox' on server 'host.net'
     * with 'granroth' as the user and 'password' as the password.
     */
    void setMailbox(KBiffURL& url);

    /**
     * Overloaded for convenience
     */
    void setMailbox(const TQString& url);
    void setMailboxKey(const TQString& k);

    /**
     * Sets the password for the POP3 and IMAP4 protocols.
     */
    void setPassword(const TQString& password);

    /**
     * Set the interval between mailbox reads.  This is in seconds.
     */
    void setPollInterval(const int interval);

    /**
     * Start monitoring the mailbox
     */
    void start();

    /**
     * Stop monitoring the mailbox
     */
    void stop();

    /**
     * Fakes KBiffMonitor into thinking that the mailbox was just read
     */
    void setMailboxIsRead();

    /**
     * Forces a mailbox check
     */
    void checkMailNow();

signals:
    /**
     * This will get <CODE>emit</CODE>ed when new mail arrives
     */
    void signal_newMail();

    /**
     * This will get <CODE>emit</CODE>ed when new mail arrives
     */
    void signal_newMail(const int num_new, const TQString& mailbox);

    /**
     * This will get <CODE>emit</CODE>ed when no mail exists
     */
    void signal_noMail();

    /**
     * This will get <CODE>emit</CODE>ed when no mail exists
     */
    void signal_noMail(const TQString& mailbox);

    /**
     * This will get <CODE>emit</CODE>ed when the mailbox is read
     */
    void signal_oldMail();

    /**
     * This will get <CODE>emit</CODE>ed when the mailbox is read
     */
    void signal_oldMail(const TQString& mailbox);

   /**
    * This will get <CODE>emit</CODE>ed when no connection can be
    * established
    */
    void signal_noConn();
               
   /**
    * This will get <CODE>emit</CODE>ed when no connection can
    * be established
    */
    void signal_noConn(const TQString& mailbox);

    /**
     * This will get <CODE>emit</CODE>ed everytime mail will be
     * fetched externally
     */
    void signal_fetchMail(const TQString& fetchClient);

    /**
     * This will get <CODE>emit</CODE>ed everytime the mailbox
     * should be checked (determined by @ref #setPollInterval)
     */
    void signal_checkMail();

    /**
     * This will get <CODE>emit</CODE>ed everytime the mailbox is
     * checked.  It contains the current mailbox name, state, and number
     * of new messages
     */
    void signal_currentStatus(const int, const TQString& , const KBiffMailState);

    /**
     * This will get <CODE>emit</CODE>ed everytime there was an
     * invalid login or incomplete connection to a remote server.
     */
    void signal_invalidLogin(const TQString& mailbox);

protected:
    void timerEvent(TQTimerEvent *);

protected slots:
    void checkLocal();
    void checkMbox();
    void checkPop();
    void checkMaildir();
    void checkImap();
    void checkMHdir();
    void checkNntp();

protected:
    // protected (non-slot) functions
    void determineState(unsigned int size, const TQDateTime& last_read,
                        const TQDateTime& last_modified);
    void determineState(unsigned int size);
    
    void determineState(KBiffUidlList uidl_list);
    void determineState(KBiffMailState state);
    void onStateChanged();
    int  mboxMessages();

    void invalidLogin();

private:
    // General stuff
    int     poll;
    int     oldTimer;
    bool    started;
    int     newCount;
    int     curCount;
    int     oldCount;
    bool    firstRun;

    // Mailbox stuff
    TQString key;
    TQString simpleURL;
    TQString protocol;
    TQString mailbox;
    TQString server;
    TQString user;
    TQString password;
    TQString fetchCommand;
    unsigned short int port;
    bool    preauth;
    bool    keepalive;

    // New state cache
    unsigned int   new_lastSize;
    TQDateTime      new_lastRead;
    TQDateTime      new_lastModified;
    KBiffUidlList  new_uidlList;
    bool           b_new_lastSize;
    bool           b_new_lastRead;
    bool           b_new_lastModified;
    bool           b_new_uidlList;
    // State variables
    KBiffMailState mailState;
    unsigned int   lastSize;
    TQDateTime      lastRead;
    TQDateTime      lastModified;
    KBiffUidlList  uidlList;

    // Socket protocols
    KBiffImap      *imap;
    KBiffPop       *pop;
    KBiffNntp      *nntp;
};

#endif // KBIFFMONITOR_H
