• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeutils API Reference
  • KDE Home
  • Contact Us
 

ark

  • sources
  • kde-4.14
  • kdeutils
  • ark
  • plugins
  • clirarplugin
clirarplugin/cliplugin.cpp
Go to the documentation of this file.
1 /*
2  * ark -- archiver for the KDE project
3  *
4  * Copyright (C) 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
5  * Copyright (C) 2010-2011,2014 Raphael Kubo da Costa <rakuco@FreeBSD.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "cliplugin.h"
24 #include "kerfuffle/cliinterface.h"
25 #include "kerfuffle/kerfuffle_export.h"
26 
27 #include <KDebug>
28 
29 #include <QDateTime>
30 #include <QDir>
31 #include <QString>
32 #include <QStringList>
33 
34 using namespace Kerfuffle;
35 
36 CliPlugin::CliPlugin(QObject *parent, const QVariantList& args)
37  : CliInterface(parent, args)
38  , m_parseState(ParseStateColumnDescription1)
39  , m_isPasswordProtected(false)
40  , m_remainingIgnoredSubHeaderLines(0)
41  , m_remainingIgnoredDetailsLines(0)
42  , m_isUnrarFree(false)
43  , m_isUnrarVersion5(false)
44 {
45 }
46 
47 CliPlugin::~CliPlugin()
48 {
49 }
50 
51 // #272281: the proprietary unrar program does not like trailing '/'s
52 // in directories passed to it when extracting only part of
53 // the files in an archive.
54 QString CliPlugin::escapeFileName(const QString &fileName) const
55 {
56  if (fileName.endsWith(QLatin1Char('/'))) {
57  return fileName.left(fileName.length() - 1);
58  }
59 
60  return fileName;
61 }
62 
63 ParameterList CliPlugin::parameterList() const
64 {
65  static ParameterList p;
66 
67  if (p.isEmpty()) {
68  p[CaptureProgress] = true;
69  p[ListProgram] = p[ExtractProgram] = QStringList() << QLatin1String( "unrar" );
70  p[DeleteProgram] = p[AddProgram] = QStringList() << QLatin1String( "rar" );
71 
72  p[ListArgs] = QStringList() << QLatin1String( "vt" ) << QLatin1String( "-c-" ) << QLatin1String( "-v" ) << QLatin1String( "$Archive" );
73  p[ExtractArgs] = QStringList() << QLatin1String( "-kb" ) << QLatin1String( "-p-" )
74  << QLatin1String( "$PreservePathSwitch" )
75  << QLatin1String( "$PasswordSwitch" )
76  << QLatin1String( "$RootNodeSwitch" )
77  << QLatin1String( "$Archive" )
78  << QLatin1String( "$Files" );
79  p[PreservePathSwitch] = QStringList() << QLatin1String( "x" ) << QLatin1String( "e" );
80  p[RootNodeSwitch] = QStringList() << QLatin1String( "-ap$Path" );
81  p[PasswordSwitch] = QStringList() << QLatin1String( "-p$Password" );
82 
83  p[DeleteArgs] = QStringList() << QLatin1String( "d" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" );
84 
85  p[FileExistsExpression] = QLatin1String( "^(.+) already exists. Overwrite it" );
86  p[FileExistsInput] = QStringList()
87  << QLatin1String( "Y" ) //overwrite
88  << QLatin1String( "N" ) //skip
89  << QLatin1String( "A" ) //overwrite all
90  << QLatin1String( "E" ) //autoskip
91  << QLatin1String( "Q" ) //cancel
92  ;
93 
94  p[AddArgs] = QStringList() << QLatin1String( "a" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" );
95 
96  p[PasswordPromptPattern] = QLatin1String("Enter password \\(will not be echoed\\) for");
97 
98  p[WrongPasswordPatterns] = QStringList() << QLatin1String("password incorrect") << QLatin1String("wrong password");
99  p[ExtractionFailedPatterns] = QStringList() << QLatin1String( "CRC failed" ) << QLatin1String( "Cannot find volume" );
100  }
101 
102  return p;
103 }
104 
105 bool CliPlugin::readListLine(const QString &line)
106 {
107  static const QLatin1String headerString("----------------------");
108  static const QLatin1String subHeaderString("Data header type: ");
109  static const QLatin1String columnDescription1String(" Size Packed Ratio Date Time Attr CRC Meth Ver");
110  static const QLatin1String columnDescription2String(" Host OS Solid Old"); // Only present in unrar-nonfree
111 
112  if (m_isUnrarVersion5) {
113  int colonPos = line.indexOf(QLatin1Char(':'));
114  if (colonPos == -1) {
115  if (m_entryFileName.isEmpty()) {
116  return true;
117  }
118  ArchiveEntry e;
119 
120  QString compressionRatio = m_entryDetails.value(QLatin1String("ratio"));
121  compressionRatio.chop(1); // Remove the '%'
122 
123  QString time = m_entryDetails.value(QLatin1String("mtime"));
124  // FIXME unrar 5 beta 8 seems to lack the seconds, or the trailing ,000 is not the milliseconds
125  QDateTime ts = QDateTime::fromString(time, QLatin1String("yyyy-MM-dd HH:mm,zzz"));
126 
127  bool isDirectory = m_entryDetails.value(QLatin1String("type")) == QLatin1String("Directory");
128  if (isDirectory && !m_entryFileName.endsWith(QLatin1Char( '/' ))) {
129  m_entryFileName += QLatin1Char( '/' );
130  }
131 
132  QString compression = m_entryDetails.value(QLatin1String("compression"));
133  int optionPos = compression.indexOf(QLatin1Char('-'));
134  if (optionPos != -1) {
135  e[Method] = compression.mid(optionPos);
136  e[Version] = compression.left(optionPos).trimmed();
137  } else {
138  // no method specified
139  e[Method].clear();
140  e[Version] = compression;
141  }
142 
143  m_isPasswordProtected = m_entryDetails.value(QLatin1String("flags")).contains(QLatin1String("encrypted"));
144 
145  e[FileName] = m_entryFileName;
146  e[InternalID] = m_entryFileName;
147  e[Size] = m_entryDetails.value(QLatin1String("size"));
148  e[CompressedSize] = m_entryDetails.value(QLatin1String("packed size"));
149  e[Ratio] = compressionRatio;
150  e[Timestamp] = ts;
151  e[IsDirectory] = isDirectory;
152  e[Permissions] = m_entryDetails.value(QLatin1String("attributes"));
153  e[CRC] = m_entryDetails.value(QLatin1String("crc32"));
154  e[IsPasswordProtected] = m_isPasswordProtected;
155  kDebug() << "Added entry: " << e;
156 
157  emit entry(e);
158 
159  m_entryFileName.clear();
160 
161  return true;
162  }
163 
164  QString key = line.left(colonPos).trimmed().toLower();
165  QString value = line.mid(colonPos + 2);
166 
167  if (key == QLatin1String("name")) {
168  m_entryFileName = value;
169  m_entryDetails.clear();
170  return true;
171  }
172 
173  // in multivolume archives, the split CRC32 is denoted specially
174  if (key == QLatin1String("pack-crc32")) {
175  key = key.mid(5);
176  }
177 
178  m_entryDetails.insert(key, value);
179 
180  return true;
181  }
182 
183  switch (m_parseState)
184  {
185  case ParseStateColumnDescription1:
186  if (line.startsWith(QLatin1String("Details:"))) {
187  m_isUnrarVersion5 = true;
188  setListEmptyLines(true);
189  // no previously detected entry
190  m_entryFileName.clear();
191  }
192  if (line.startsWith(columnDescription1String)) {
193  m_parseState = ParseStateColumnDescription2;
194  }
195 
196  break;
197 
198  case ParseStateColumnDescription2:
199  // #243273: We need a way to differentiate unrar and unrar-free,
200  // as their output for the "vt" option is different.
201  // Currently, we differ them by checking if "vt" produces
202  // two lines of column names before the header string, as
203  // only unrar does that (unrar-free always outputs one line
204  // for column names regardless of how verbose we tell it to
205  // be).
206  if (line.startsWith(columnDescription2String)) {
207  m_parseState = ParseStateHeader;
208  } else if (line.startsWith(headerString)) {
209  m_parseState = ParseStateEntryFileName;
210  m_isUnrarFree = true;
211  }
212 
213  break;
214 
215  case ParseStateHeader:
216  if (line.startsWith(headerString)) {
217  m_parseState = ParseStateEntryFileName;
218  }
219 
220  break;
221 
222  case ParseStateEntryFileName:
223  if (m_remainingIgnoredSubHeaderLines > 0) {
224  --m_remainingIgnoredSubHeaderLines;
225  return true;
226  }
227 
228  // #242071: The RAR file format has the concept of service headers,
229  // such as CMT (comments), STM (NTFS alternate data streams)
230  // and RR (recovery record). These service headers do no
231  // interest us, and ignoring them seems harmless (at least
232  // 7zip and WinRAR do not show them either).
233  if (line.startsWith(subHeaderString)) {
234  // subHeaderString's length is 18
235  const QString subHeaderType(line.mid(18));
236 
237  // XXX: If we ever support archive comments, this code must
238  // be changed, because the comments will be shown after
239  // a CMT subheader and will have an arbitrary number of lines
240  if (subHeaderType == QLatin1String("STM")) {
241  m_remainingIgnoredSubHeaderLines = 4;
242  } else {
243  m_remainingIgnoredSubHeaderLines = 3;
244  }
245 
246  kDebug() << "Found a subheader of type" << subHeaderType;
247  kDebug() << "The next" << m_remainingIgnoredSubHeaderLines
248  << "lines will be ignored";
249 
250  return true;
251  } else if (line.startsWith(headerString)) {
252  m_parseState = ParseStateHeader;
253 
254  return true;
255  }
256 
257  m_isPasswordProtected = (line.at(0) == QLatin1Char( '*' ));
258 
259  // Start from 1 because the first character is either ' ' or '*'
260  m_entryFileName = QDir::fromNativeSeparators(line.mid(1));
261 
262  m_parseState = ParseStateEntryDetails;
263 
264  break;
265 
266  case ParseStateEntryIgnoredDetails:
267  if (m_remainingIgnoredDetailsLines > 0) {
268  --m_remainingIgnoredDetailsLines;
269  return true;
270  }
271  m_parseState = ParseStateEntryFileName;
272 
273  break;
274 
275  case ParseStateEntryDetails:
276  if (line.startsWith(headerString)) {
277  m_parseState = ParseStateHeader;
278  return true;
279  }
280 
281  const QStringList details = line.split(QLatin1Char( ' ' ),
282  QString::SkipEmptyParts);
283 
284  QDateTime ts(QDate::fromString(details.at(3),
285  QLatin1String("dd-MM-yy")),
286  QTime::fromString(details.at(4),
287  QLatin1String("hh:mm")));
288 
289  // unrar outputs dates with a 2-digit year but QDate takes it as 19??
290  // let's take 1950 is cut-off; similar to KDateTime
291  if (ts.date().year() < 1950) {
292  ts = ts.addYears(100);
293  }
294 
295  bool isDirectory = ((details.at(5).at(0) == QLatin1Char( 'd' )) ||
296  (details.at(5).at(1) == QLatin1Char( 'D' )));
297  if (isDirectory && !m_entryFileName.endsWith(QLatin1Char( '/' ))) {
298  m_entryFileName += QLatin1Char( '/' );
299  }
300 
301  // If the archive is a multivolume archive, a string indicating
302  // whether the archive's position in the volume is displayed
303  // instead of the compression ratio.
304  QString compressionRatio = details.at(2);
305  if ((compressionRatio == QLatin1String("<--")) ||
306  (compressionRatio == QLatin1String("<->")) ||
307  (compressionRatio == QLatin1String("-->"))) {
308  compressionRatio = QLatin1Char( '0' );
309  } else {
310  compressionRatio.chop(1); // Remove the '%'
311  }
312 
313  // TODO:
314  // - Permissions differ depending on the system the entry was added
315  // to the archive.
316  // - unrar reports the ratio as ((compressed size * 100) / size);
317  // we consider ratio as (100 * ((size - compressed size) / size)).
318  ArchiveEntry e;
319  e[FileName] = m_entryFileName;
320  e[InternalID] = m_entryFileName;
321  e[Size] = details.at(0);
322  e[CompressedSize] = details.at(1);
323  e[Ratio] = compressionRatio;
324  e[Timestamp] = ts;
325  e[IsDirectory] = isDirectory;
326  e[Permissions] = details.at(5);
327  e[CRC] = details.at(6);
328  e[Method] = details.at(7);
329  e[Version] = details.at(8);
330  e[IsPasswordProtected] = m_isPasswordProtected;
331  kDebug() << "Added entry: " << e;
332 
333  // #314297: When RAR 3.x and RAR 4.x list a symlink, they output an
334  // extra line after the "Host OS/Solid/Old" one mentioning the
335  // target of the symlink in question. We are not interested in
336  // this line at the moment, so we just tell the parser to skip
337  // it.
338  if (e[Permissions].toString().startsWith(QLatin1Char('l'))) {
339  m_remainingIgnoredDetailsLines = 1;
340  } else {
341  m_remainingIgnoredDetailsLines = 0;
342  }
343 
344  emit entry(e);
345 
346  // #243273: unrar-free does not output the third file entry line,
347  // skip directly to parsing a new entry.
348  if (m_isUnrarFree) {
349  m_parseState = ParseStateEntryFileName;
350  } else {
351  m_parseState = ParseStateEntryIgnoredDetails;
352  }
353 
354  break;
355  }
356 
357  return true;
358 }
359 
360 KERFUFFLE_EXPORT_PLUGIN(CliPlugin)
361 
362 #include "cliplugin.moc"
Kerfuffle::Ratio
The compression ratio for the entry.
Definition: archive.h:67
cliinterface.h
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
Kerfuffle::ExtractionFailedPatterns
QStringList Default: empty A list of regexp patterns that will cause the extraction to exit with a ge...
Definition: cliinterface.h:194
QHash::insert
iterator insert(const Key &key, const T &value)
Kerfuffle::ExtractProgram
QStringList The names to the program that will handle extracting of this archive (eg "rar")...
Definition: cliinterface.h:84
Kerfuffle::IsPasswordProtected
The entry is password-protected.
Definition: archive.h:74
QDateTime::addYears
QDateTime addYears(int nyears) const
QDir::fromNativeSeparators
QString fromNativeSeparators(const QString &pathName)
Kerfuffle::IsDirectory
The entry is a directory.
Definition: archive.h:72
CliPlugin::escapeFileName
virtual QString escapeFileName(const QString &fileName) const
Performs any additional escaping and processing on fileName before passing it to the underlying proce...
Definition: clirarplugin/cliplugin.cpp:54
Kerfuffle::AddProgram
QStringList The names to the program that will handle adding in this archive format (eg "rar")...
Definition: cliinterface.h:210
kerfuffle_export.h
QString::split
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
Kerfuffle::CliInterface
Definition: cliinterface.h:224
CliPlugin
Definition: cli7zplugin/cliplugin.h:29
Kerfuffle::ListArgs
QStringList The arguments that are passed to the program above for listing the archive.
Definition: cliinterface.h:75
QList::at
const T & at(int i) const
Kerfuffle::PasswordPromptPattern
QString Default: empty A regexp pattern that matches the program's password prompt.
Definition: cliinterface.h:58
Kerfuffle::CompressedSize
The compressed size for the entry.
Definition: archive.h:65
QTime::fromString
QTime fromString(const QString &string, Qt::DateFormat format)
QString::chop
void chop(int n)
Kerfuffle::Version
The archiver version needed to extract the entry.
Definition: archive.h:70
Kerfuffle::ReadOnlyArchiveInterface::entry
void entry(const ArchiveEntry &archiveEntry)
QString::clear
void clear()
QDate::fromString
QDate fromString(const QString &string, Qt::DateFormat format)
Kerfuffle::ArchiveEntry
QHash< int, QVariant > ArchiveEntry
Definition: archive.h:78
CliPlugin::parameterList
virtual Kerfuffle::ParameterList parameterList() const
Definition: cli7zplugin/cliplugin.cpp:47
Kerfuffle::PreservePathSwitch
QStringList This should be a qstringlist with either two elements.
Definition: cliinterface.h:122
QHash
QObject
Kerfuffle::WrongPasswordPatterns
QStringList Default: empty A list of regexp patterns that will alert the user that the password was w...
Definition: cliinterface.h:201
QString::isEmpty
bool isEmpty() const
QString::trimmed
QString trimmed() const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
Kerfuffle::ExtractArgs
QStringList The arguments that are passed to the program above for extracting the archive...
Definition: cliinterface.h:100
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
Kerfuffle::FileExistsExpression
QString This is a regexp, defining how to recognize a "File already exists" prompt when extracting...
Definition: cliinterface.h:148
QDate::year
int year() const
CliPlugin::CliPlugin
CliPlugin(QObject *parent, const QVariantList &args)
Definition: cli7zplugin/cliplugin.cpp:36
QString
Kerfuffle::Timestamp
The timestamp for the current entry.
Definition: archive.h:71
CliPlugin::~CliPlugin
virtual ~CliPlugin()
Definition: cli7zplugin/cliplugin.cpp:43
QStringList
Kerfuffle::FileName
The entry's file name.
Definition: archive.h:59
QHash::clear
void clear()
QString::toLower
QString toLower() const
QHash::value
const T value(const Key &key) const
Kerfuffle::CRC
The entry's CRC.
Definition: archive.h:68
QLatin1Char
Kerfuffle::CaptureProgress
Bool (default false) Will look for the %-sign in the stdout while working, in the form of (2%...
Definition: cliinterface.h:51
QDateTime::fromString
QDateTime fromString(const QString &string, Qt::DateFormat format)
Kerfuffle::Permissions
The entry's permissions.
Definition: archive.h:61
Kerfuffle::DeleteArgs
QStringList The arguments that are passed to the program above for deleting from the archive...
Definition: cliinterface.h:187
Kerfuffle::PasswordSwitch
QStringList (default empty) The format of the root node switch.
Definition: cliinterface.h:141
QString::mid
QString mid(int position, int n) const
QDateTime::date
QDate date() const
QTest::toString
char * toString(const T &value)
Kerfuffle::DeleteProgram
QStringList The names to the program that will handle deleting of elements in this archive format (eg...
Definition: cliinterface.h:178
QLatin1String
QHash::isEmpty
bool isEmpty() const
Kerfuffle::CliInterface::setListEmptyLines
void setListEmptyLines(bool emptyLines)
Sets if the listing should include empty lines.
Definition: cliinterface.cpp:83
Kerfuffle::FileExistsInput
QStringList The various responses that can be supplied as a response to the "file exists" prompt...
Definition: cliinterface.h:169
Kerfuffle::Method
The compression method used on the entry.
Definition: archive.h:69
Kerfuffle::Size
The entry's original size.
Definition: archive.h:64
Kerfuffle::AddArgs
QStringList The arguments that are passed to the program above for adding to the archive.
Definition: cliinterface.h:219
QString::at
const QChar at(int position) const
CliPlugin::readListLine
virtual bool readListLine(const QString &line)
Definition: cli7zplugin/cliplugin.cpp:78
QString::length
int length() const
QString::left
QString left(int n) const
KERFUFFLE_EXPORT_PLUGIN
#define KERFUFFLE_EXPORT_PLUGIN(p)
Definition: kerfuffle_export.h:49
Kerfuffle::InternalID
The entry's ID for Ark's internal manipulation.
Definition: archive.h:60
Kerfuffle::RootNodeSwitch
QStringList (default empty) The format of the root node switch.
Definition: cliinterface.h:130
cliplugin.h
QDateTime
Kerfuffle::ListProgram
QStringList The names to the program that will handle listing of this archive (eg "rar")...
Definition: cliinterface.h:67
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:42:37 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

ark

Skip menu "ark"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdeutils API Reference

Skip menu "kdeutils API Reference"
  • ark
  • filelight
  • kcalc
  • kcharselect
  • kdf
  • kfloppy
  • kgpg
  • ktimer
  • kwallet
  • sweeper

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal