command.h 10.2 KB
Newer Older
Laurent Montel's avatar
Laurent Montel committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*  -*- c++ -*-
    command.h

    This file is part of kio_smtp, the KDE SMTP kioslave.
    Copyright (c) 2003 Marc Mutz <mutz@kde.org>

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#ifndef __KIOSMTP_COMMAND_H__
#define __KIOSMTP_COMMAND_H__

35 36 37
// workaround a bug in Cyrus-SASL 2.1.26 which is missing sys/types.h
// include in sasl.h
#include <sys/types.h>
Laurent Montel's avatar
Laurent Montel committed
38 39 40 41 42 43
extern "C" {
#include <sasl/sasl.h>
}

#include <kio/authinfo.h>

Laurent Montel's avatar
Laurent Montel committed
44
namespace KioSMTP {
Laurent Montel's avatar
Laurent Montel committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
class Response;
class TransactionState;
class SMTPSessionInterface;

/**
 * @short Represents an SMTP command
 *
 * Semantics: A command consists of a series of "command lines"
 * (though that's stretching it a bit for @ref TransferJob and @ref
 * AuthCommand) and responses. There's typically one response for
 * one command line and the command is completed.
 *
 * However, some commands consist of a dialog (command line,
 * response, command line, response,...) where each successive
 * command line is dependant on the previously received response
 * (and thus those commands are not pipelinable). That's why each
 * command signals completion by having it's @ref #isComplete()
 * method return true @em after the last command line to be sent,
 * but @em before the last response to receive. @ref AuthCommand is
 * the principal representative of this kind of command. Because
 * @ref EHLOCommand automatically falls back to HELO in case EHLO
 * isn't supported, it is also of this kind. If completion is
 * signalled before the first command line is issued, it is not to
 * be executed at all.
 *
 * Other commands need to send multiple "command lines" before
 * receiving a single (final) response. @ref TransferCommand is the
 * only representative of this kind of "command". That's why each
 * command signals whether it now expects a response before being
 * able to issue the next command line (if any) by having it's @ref
 * #needsResponse() method return true.
 *
 * Commands whose @ref #nextCommandLine() does not support being
 * called multiple times in a row without changing command state,
 * must reimplement @ref #ungetCommandLine().
 **/
class Command
{
public:
    enum Flags {
        OnlyLastInPipeline = 1,
        OnlyFirstInPipeline = 2,
        CloseConnectionOnError = 4
    };

    explicit Command(SMTPSessionInterface *smtp, int flags = 0);
    virtual ~Command();

    enum Type {
        STARTTLS,
        DATA,
        NOOP,
        RSET,
        QUIT
    };

    static Command *createSimpleCommand(int which, SMTPSessionInterface *smtp);

    virtual QByteArray nextCommandLine(TransactionState *ts = 0) = 0;
    /* Reimplement this if your @ref #nextCommandLine() implementation
       changes state other than @ref mComplete. The default
       implementation just resets @ref mComplete to false. */
    virtual void ungetCommandLine(const QByteArray &cmdLine, TransactionState *ts = 0);
    /* Reimplement this if your command need more sophisicated
       response processing than just checking for @ref
       Response::isOk(). The default implementation sets @ref
       mComplete to true, @ref mNeedResponse to false and returns
       whether the response isOk(). */
    virtual bool processResponse(const Response &response, TransactionState *ts = 0);

    virtual bool doNotExecute(const TransactionState *ts) const
    {
        Q_UNUSED(ts)
        return false;
    }

    bool isComplete() const
    {
        return mComplete;
    }

    /**
     * @return whether the command expects a response now. Some
     * commands (most notably AUTH) may consist of a series of
     * commands and associated responses until they are
     * complete. Others (most notably @ref TransferCommand usually
     * send multiple "command lines" before expecting a response.
     */
    bool needsResponse() const
    {
        return mNeedResponse;
    }

    /**
     * @return whether an error in executing this command is so fatal
     * that closing the connection is the only option
     */
    bool closeConnectionOnError() const
    {
        return mFlags & CloseConnectionOnError;
    }
Laurent Montel's avatar
Laurent Montel committed
146

Laurent Montel's avatar
Laurent Montel committed
147 148 149 150
    bool mustBeLastInPipeline() const
    {
        return mFlags & OnlyLastInPipeline;
    }
Laurent Montel's avatar
Laurent Montel committed
151

Laurent Montel's avatar
Laurent Montel committed
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    bool mustBeFirstInPipeline() const
    {
        return mFlags & OnlyFirstInPipeline;
    }

protected:
    SMTPSessionInterface *mSMTP;
    bool mComplete;
    bool mNeedResponse;
    const int mFlags;

protected:
    // only relay methods to enable access to slave-protected methods
    // for subclasses of Command:
    void parseFeatures(const Response &r);
    int startSsl();
    bool haveCapability(const char *cap) const;
};

class EHLOCommand : public Command
{
public:
    EHLOCommand(SMTPSessionInterface *smtp, const QString &hostname)
        : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
        , mEHLONotSupported(false)
        , mHostname(hostname.trimmed())
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
private:
    bool mEHLONotSupported;
    QString mHostname;
};

class StartTLSCommand : public Command
{
public:
    StartTLSCommand(SMTPSessionInterface *smtp)
        : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
};

class AuthCommand : public Command
{
public:
Laurent Montel's avatar
Laurent Montel committed
203
    AuthCommand(SMTPSessionInterface *smtp, const char *mechanisms, const QString &aFQDN, KIO::AuthInfo &ai);
Laurent Montel's avatar
Laurent Montel committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    ~AuthCommand();
    bool doNotExecute(const TransactionState *ts) const Q_DECL_OVERRIDE;
    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    void ungetCommandLine(const QByteArray &cmdLine, TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
private:
    bool saslInteract(void *in);

    sasl_conn_t *conn;
    sasl_interact_t *client_interact;
    const char *mOut;
    uint mOutlen;
    bool mOneStep;

    const char *mMechusing;
    KIO::AuthInfo *mAi;
    QByteArray mLastChallenge;
    QByteArray mUngetSASLResponse;
    bool mFirstTime;
};

class MailFromCommand : public Command
{
public:
Laurent Montel's avatar
Laurent Montel committed
228
    MailFromCommand(SMTPSessionInterface *smtp, const QByteArray &addr, bool eightBit = false, unsigned int size = 0)
Laurent Montel's avatar
Laurent Montel committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        : Command(smtp)
        , mAddr(addr)
        , m8Bit(eightBit)
        , mSize(size)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
private:
    QByteArray mAddr;
    bool m8Bit;
    unsigned int mSize;
};

class RcptToCommand : public Command
{
public:
    RcptToCommand(SMTPSessionInterface *smtp, const QByteArray &addr)
        : Command(smtp)
        , mAddr(addr)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
private:
    QByteArray mAddr;
};

/** Handles only the initial intermediate response and compltetes at
    the point where the mail contents need to be sent */
class DataCommand : public Command
{
public:
    DataCommand(SMTPSessionInterface *smtp)
        : Command(smtp, OnlyLastInPipeline)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    void ungetCommandLine(const QByteArray &cmd, TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
};

/** Handles the data transfer following a successful DATA command */
class TransferCommand : public Command
{
public:
    TransferCommand(SMTPSessionInterface *smtp, const QByteArray &initialBuffer)
        : Command(smtp, OnlyFirstInPipeline)
        , mUngetBuffer(initialBuffer)
        , mLastChar('\n')
        , mWasComplete(false)
    {
    }

    bool doNotExecute(const TransactionState *ts) const Q_DECL_OVERRIDE;
    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
    void ungetCommandLine(const QByteArray &cmd, TransactionState *ts) Q_DECL_OVERRIDE;
    bool processResponse(const Response &response, TransactionState *ts) Q_DECL_OVERRIDE;
private:
    QByteArray prepare(const QByteArray &ba);
    QByteArray mUngetBuffer;
    char mLastChar;
    bool mWasComplete; // ... before ungetting
};

class NoopCommand : public Command
{
public:
    NoopCommand(SMTPSessionInterface *smtp)
        : Command(smtp, OnlyLastInPipeline)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
};

class RsetCommand : public Command
{
public:
    RsetCommand(SMTPSessionInterface *smtp)
        : Command(smtp, CloseConnectionOnError)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
};

class QuitCommand : public Command
{
public:
    QuitCommand(SMTPSessionInterface *smtp)
        : Command(smtp, CloseConnectionOnError | OnlyLastInPipeline)
    {
    }

    QByteArray nextCommandLine(TransactionState *ts) Q_DECL_OVERRIDE;
};
} // namespace KioSMTP

#endif // __KIOSMTP_COMMAND_H__