cliinterface.h 14.5 KB
Newer Older
1 2 3
/*
 * ark -- archiver for the KDE project
 *
4
 * Copyright (C) 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
5
 * Copyright (C) 2009-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
6
 *
7 8 9
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
10
 *
11 12 13 14 15
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
16
 *
17 18 19 20 21 22 23 24 25 26
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28 29
#ifndef CLIINTERFACE_H
#define CLIINTERFACE_H
30 31 32

#include "archiveinterface.h"
#include "kerfuffle_export.h"
Elvis Angelaccio's avatar
Elvis Angelaccio committed
33 34

#include <QProcess>
35
#include <QRegularExpression>
36 37

class KProcess;
38
class KPtyProcess;
39 40

class QDir;
41
class QTemporaryDir;
42
class QTemporaryFile;
43 44 45 46

namespace Kerfuffle
{

47
enum CliInterfaceParameters {
Harald Hvaal's avatar
Harald Hvaal committed
48

49
    ///////////////[ COMMON ]/////////////
Harald Hvaal's avatar
Harald Hvaal committed
50

51 52 53 54 55 56
    /**
     * Bool (default false)
     * Will look for the %-sign in the stdout while working, in the form of
     * (2%, 14%, 35%, etc etc), and report progress based upon this
     */
    CaptureProgress = 0,
Harald Hvaal's avatar
Harald Hvaal committed
57

58 59 60 61 62 63 64
    /**
     * QString
     * Default: empty
     * A regexp pattern that matches the program's password prompt.
     */
    PasswordPromptPattern,

65
    ///////////////[ LIST ]/////////////
Harald Hvaal's avatar
Harald Hvaal committed
66

67
    /**
68 69
     * QStringList
     * The names to the program that will handle listing of this
70 71 72 73 74 75 76 77 78 79 80
     * archive (eg "rar"). Will be searched for in PATH
     */
    ListProgram,
    /**
     * QStringList
     * The arguments that are passed to the program above for
     * listing the archive. Special strings that will be
     * substituted:
     * $Archive - the path of the archive
     */
    ListArgs,
81 82 83 84 85
    /**
     * QStringList (default empty)
     * List of regexp patterns that indicate a corrupt archive.
     */
    CorruptArchivePatterns,
Harald Hvaal's avatar
Harald Hvaal committed
86

87
    ///////////////[ EXTRACT ]/////////////
Harald Hvaal's avatar
Harald Hvaal committed
88

89
    /**
90 91
     * QStringList
     * The names to the program that will handle extracting of this
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
     * archive (eg "rar"). Will be searched for in PATH
     */
    ExtractProgram,
    /**
     * QStringList
     * The arguments that are passed to the program above for
     * extracting the archive. Special strings that will be
     * substituted:
     * $Archive - the path of the archive
     * $Files - the files selected to be extracted, if any
     * $PreservePathSwitch - the flag for extracting with full paths
     * $PasswordSwitch - the switch setting the password. Note that this
     * will not be inserted unless the listing function has emitted an
     * entry with the IsPasswordProtected property set to true.
     */
    ExtractArgs,
    /**
     * Bool (default false)
     * When passing directories to the extract program, do not
     * include trailing slashes
     * e.g. if the user selected "foo/" and "foo/bar" in the gui, the
     * paths "foo" and "foo/bar" will be sent to the program.
     */
    NoTrailingSlashes,
    /**
     * QStringList
     * This should be a qstringlist with either two elements. The first
     * string is what PreservePathSwitch in the ExtractArgs will be replaced
     * with if PreservePath is True/enabled. The second is for the disabled
     * case. An empty string means that the argument will not be used in
     * that case.
     * Example: for rar, "x" means extract with full paths, and "e" means
     * extract without full paths. in this case we will use the stringlist
     * ("x", "e"). Or, for another format that might use the switch
     * "--extractFull" for preservePaths, and nothing otherwise: we use the
127
     * stringlist ("--extractFull", "")
128 129 130 131 132 133 134 135 136 137 138 139 140
     */
    PreservePathSwitch,
    /**
     * QStringList (default empty)
     * The format of the root node switch. The variable $Password will be
     * substituted for the password string. NOTE: supplying passwords
     * through a virtual terminal is not supported (yet?), because this
     * is not cross platform compatible. As of KDE 4.3 there are no plans to
     * change this.
     * Example: ("-p$Password)
     * or ("--password", "$Password")
     */
    PasswordSwitch,
141 142 143 144 145 146 147
    /**
     * QString
     * The format of the compression level switch. The variable $CompressionLevel
     * will be substituted for the level.
     * Example: ("-mx=$CompressionLevel)
     */
    CompressionLevelSwitch,
148
    /**
149 150 151
     * QStringList
     * This is a stringlist with regexps, defining how to recognize the last
     * line in a "File already exists" prompt when extracting.
152 153
     */
    FileExistsExpression,
154 155 156 157 158 159 160 161
    /**
     * QStringList
     * This is a stringlist with regexps defining how to recognize the line
     * containing the filename in a "File already exists" prompt when
     * extracting. It should have one captured string, which is the filename
     * of the file/folder that already exists.
     */
    FileExistsFileName,
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    /**
     * int
     * This sets on what output channel the FileExistsExpression regex
     * should be applied on, in other words, on what stream the "file
     * exists" output will appear in. Values accepted:
     * 0 - Standard error, stderr (default)
     * 1 - Standard output, stdout
     */
    FileExistsMode,
    /**
     * QStringList
     * The various responses that can be supplied as a response to the
     * "file exists" prompt. The various items are to be supplied in the
     * following order:
     * index 0 - Yes (overwrite)
     * index 1 - No (skip/do not overwrite)
     * index 2 - All (overwrite all)
179 180
     * index 3 - Do not overwrite any files (autoskip)
     * index 4 - Cancel operation
181 182
     */
    FileExistsInput,
183 184 185 186 187
    /**
      * QStringList
      * Regexp patterns capturing disk is full error messages.
      */
    DiskFullPatterns,
Harald Hvaal's avatar
Harald Hvaal committed
188

189
    ///////////////[ DELETE ]/////////////
Harald Hvaal's avatar
Harald Hvaal committed
190

191
    /**
192 193
     * QStringList
     * The names to the program that will handle deleting of elements in this
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
     * archive format (eg "rar"). Will be searched for in PATH
     */
    DeleteProgram,
    /**
     * QStringList
     * The arguments that are passed to the program above for
     * deleting from the archive. Special strings that will be
     * substituted:
     * $Archive - the path of the archive
     * $Files - the files selected to be deleted
     */
    DeleteArgs,
    /**
     * QStringList
     * Default: empty
     * A list of regexp patterns that will cause the extraction to exit
     * with a general fail message
     */
    ExtractionFailedPatterns,
    /**
     * QStringList
     * Default: empty
     * A list of regexp patterns that will alert the user that the password
     * was wrong.
     */
    WrongPasswordPatterns,
220

221
    ///////////////[ ADD ]/////////////
222

223
    /**
224 225
     * QStringList
     * The names to the program that will handle adding in this
226 227 228 229 230 231 232 233 234 235 236
     * archive format (eg "rar"). Will be searched for in PATH
     */
    AddProgram,
    /**
     * QStringList
     * The arguments that are passed to the program above for
     * adding to the archive. Special strings that will be
     * substituted:
     * $Archive - the path of the archive
     * $Files - the files selected to be added
     */
237 238 239 240 241 242 243 244 245 246 247
    AddArgs,

    ///////////////[ ENCRYPT ]/////////////

    /**
     * QStringList (default empty)
     * The variable $Password will be
     * substituted for the password string used to encrypt the header.
     * Example (rar plugin): ("-hp$Password")
     */
    PasswordHeaderSwitch,
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262

    ///////////////[ COMMENT ]/////////////

    /**
     * QStringList
     * The arguments that are passed to AddProgram when adding
     * a comment.
     */
    CommentArgs,
    /**
     * QString
     * The variable $CommentFile will be substituted for the file
     * containing the comment.
     * Example (rar plugin): -z$CommentFile
     */
Ragnar Thomsen's avatar
Ragnar Thomsen committed
263 264 265 266
    CommentSwitch,
    TestProgram,
    TestArgs,
    TestPassedPattern
267
};
268

269
typedef QHash<int, QVariant> ParameterList;
270

271 272 273 274 275 276
class KERFUFFLE_EXPORT CliInterface : public ReadWriteArchiveInterface
{
    Q_OBJECT

public:
    enum OperationMode  {
Ragnar Thomsen's avatar
Ragnar Thomsen committed
277
        List, Copy, Add, Delete, Comment, Test
278
    };
279
    OperationMode m_operationMode;
Harald Hvaal's avatar
Harald Hvaal committed
280

281
    explicit CliInterface(QObject *parent, const QVariantList & args);
282
    virtual ~CliInterface();
283

284
    virtual bool list() Q_DECL_OVERRIDE;
285
    virtual bool copyFiles(const QList<QVariant>& files, const QString& destinationDirectory, const ExtractionOptions& options) Q_DECL_OVERRIDE;
286 287
    virtual bool addFiles(const QStringList & files, const CompressionOptions& options) Q_DECL_OVERRIDE;
    virtual bool deleteFiles(const QList<QVariant> & files) Q_DECL_OVERRIDE;
288
    virtual bool addComment(const QString &comment) Q_DECL_OVERRIDE;
Ragnar Thomsen's avatar
Ragnar Thomsen committed
289
    virtual bool testArchive() Q_DECL_OVERRIDE;
290

291
    virtual void resetParsing() = 0;
292
    virtual ParameterList parameterList() const = 0;
293
    virtual bool readListLine(const QString &line) = 0;
294 295 296
    bool doKill() Q_DECL_OVERRIDE;
    bool doSuspend() Q_DECL_OVERRIDE;
    bool doResume() Q_DECL_OVERRIDE;
297

Christoph Feck's avatar
Christoph Feck committed
298 299 300 301 302 303 304
    /**
     * Sets if the listing should include empty lines.
     *
     * The default value is false.
     */
    void setListEmptyLines(bool emptyLines);

305 306
    /**
     * Move all files from @p tmpDir to @p destDir, preserving paths if @p preservePaths is true.
Elvis Angelaccio's avatar
Elvis Angelaccio committed
307
     * @return Whether the operation has been successful.
308
     */
309
    bool moveToDestination(const QDir &tempDir, const QDir &destDir, bool preservePaths);
310

311
    QStringList substituteListVariables(const QStringList &listArgs, const QString &password);
312
    QStringList substituteCopyVariables(const QStringList &extractArgs, const QVariantList &files, bool preservePaths, const QString &password);
313
    QStringList substituteAddVariables(const QStringList &addArgs, const QStringList &files, const QString &password, bool encryptHeader, int compLevel);
314
    QStringList substituteDeleteVariables(const QStringList &deleteArgs, const QVariantList &files, const QString &password);
315
    QStringList substituteCommentVariables(const QStringList &commentArgs, const QString &commentFile);
316
    QStringList substituteTestVariables(const QStringList &testArgs, const QString &password);
317 318 319 320 321 322

    /**
     * @return The preserve path switch, according to the @p preservePaths extraction option.
     */
    QString preservePathSwitch(bool preservePaths) const;

323 324 325 326 327
    /**
     * @return The password header-switch with the given @p password.
     */
    virtual QStringList passwordHeaderSwitch(const QString& password) const;

328 329 330 331 332
    /**
     * @return The password switch with the given @p password.
     */
    QStringList passwordSwitch(const QString& password) const;

333 334 335 336 337
    /**
     * @return The compression level switch with the given @p level.
     */
    QString compressionLevelSwitch(int level) const;

338 339 340 341 342
    /**
     * @return The list of selected files to extract.
     */
    QStringList copyFilesList(const QVariantList& files) const;

343
protected:
344

345
    virtual void handleLine(const QString& line);
346
    virtual void cacheParameterList();
347 348 349 350 351 352 353

    /**
     * Run @p programName with the given @p arguments.
     *
     * @param programName The program that will be run (not the whole path).
     * @param arguments A list of arguments that will be passed to the program.
     *
354 355
     * @return @c true if the program was found and the process was started correctly,
     *         @c false otherwise (in which case finished(false) is emitted).
356 357 358
     */
    bool runProcess(const QStringList& programNames, const QStringList& arguments);

359 360 361 362
    /**
     * Kill the running process. The finished signal is emitted according to @p emitFinished.
     */
    void killProcess(bool emitFinished = true);
363

364 365 366 367 368 369
    /**
     * Ask the password *before* running any process.
     * @return True if the user supplies a password, false otherwise (in which case finished() is emitted).
     */
    bool passwordQuery();

370 371 372 373 374 375 376
    ParameterList m_param;
    int m_exitCode;

protected slots:
    virtual void readStdout(bool handleAll = false);

private:
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

    /**
     * Checks whether a line of the program's output is a password prompt.
     *
     * It uses the regular expression in the @c PasswordPromptPattern parameter
     * for the check.
     *
     * @param line A line of the program's output.
     *
     * @return @c true if the given @p line is a password prompt, @c false
     * otherwise.
     */

    bool checkForPasswordPromptMessage(const QString& line);

392 393
    bool handleFileExistsMessage(const QString& filename);
    bool checkForErrorMessage(const QString& line, int parameterIndex);
Ragnar Thomsen's avatar
Ragnar Thomsen committed
394
    bool checkForTestSuccessMessage(const QString& line);
395

396
    /**
397 398 399 400
     * Performs any additional escaping and processing on @p fileName
     * before passing it to the underlying process.
     *
     * The default implementation returns @p fileName unchanged.
401 402 403
     *
     * @param fileName String to escape.
     */
404
    virtual QString escapeFileName(const QString &fileName) const;
405

406 407 408 409 410 411
    /**
     * Wrapper around KProcess::write() or KPtyDevice::write(), depending on
     * the platform.
     */
    void writeToProcess(const QByteArray& data);

412
    bool moveDroppedFilesToDest(const QVariantList &files, const QString &finalDest);
413

414 415 416 417 418
    /**
     * @return Whether @p dir is an empty directory.
     */
    bool isEmptyDir(const QDir &dir);

419 420
    void copyProcessCleanup();

421
    QByteArray m_stdOutData;
422 423
    QRegularExpression m_passwordPromptPattern;
    QHash<int, QList<QRegularExpression> > m_patternCache;
424

425
#ifdef Q_OS_WIN
426
    KProcess *m_process;
427 428 429 430
#else
    KPtyProcess *m_process;
#endif

431
    QVariantList m_removedFiles;
Christoph Feck's avatar
Christoph Feck committed
432
    bool m_listEmptyLines;
433
    bool m_abortingOperation;
434
    QString m_storedFileName;
435

436 437 438 439
    CompressionOptions m_compressionOptions;
    QString m_oldWorkingDir;
    QString m_extractDestDir;
    QTemporaryDir *m_extractTempDir;
440
    QTemporaryFile *m_commentTempFile;
441 442
    QVariantList m_copiedFiles;

443 444
private slots:
    void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
445 446
    void copyProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);

447
};
448 449
}

450
#endif /* CLIINTERFACE_H */