Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F37954806
archivedefinition.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
16 KB
Subscribers
None
archivedefinition.cpp
View Options
/* -*- mode: c++; c-basic-offset:4 -*-
utils/archivedefinition.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2009, 2010 Klarälvdalens Datakonsult AB
Kleopatra is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Kleopatra 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.
*/
#include
<config-kleopatra.h>
#include
"archivedefinition.h"
#include
<utils/input.h>
#include
<utils/output.h>
#include
<utils/path-helper.h>
#include
<utils/kleo_assert.h>
#include
<gpgme++/exception.h>
#include
<KSharedConfig>
#include
<KConfigGroup>
#include
"kleopatra_debug.h"
#include
<KLocalizedString>
#include
<KConfig>
#include
<KShell>
#include
<QString>
#include
<QStringList>
#include
<QDir>
#include
<QMutex>
#include
<QCoreApplication>
#include
<QRegularExpression>
#include
<QStandardPaths>
using
namespace
GpgME
;
using
namespace
Kleo
;
static
QMutex
installPathMutex
;
Q_GLOBAL_STATIC
(
QString
,
_installPath
)
QString
ArchiveDefinition
::
installPath
()
{
const
QMutexLocker
locker
(
&
installPathMutex
);
QString
*
const
ip
=
_installPath
();
if
(
ip
->
isEmpty
())
{
if
(
QCoreApplication
::
instance
())
{
*
ip
=
QCoreApplication
::
applicationDirPath
();
}
else
{
qCWarning
(
KLEOPATRA_LOG
)
<<
"called before QCoreApplication was constructed"
;
}
}
return
*
ip
;
}
void
ArchiveDefinition
::
setInstallPath
(
const
QString
&
ip
)
{
const
QMutexLocker
locker
(
&
installPathMutex
);
*
_installPath
()
=
ip
;
}
// Archive Definition #N groups
static
const
QLatin1String
ID_ENTRY
(
"id"
);
static
const
QLatin1String
NAME_ENTRY
(
"Name"
);
static
const
QLatin1String
PACK_COMMAND_ENTRY
(
"pack-command"
);
static
const
QLatin1String
PACK_COMMAND_OPENPGP_ENTRY
(
"pack-command-openpgp"
);
static
const
QLatin1String
PACK_COMMAND_CMS_ENTRY
(
"pack-command-cms"
);
static
const
QLatin1String
UNPACK_COMMAND_ENTRY
(
"unpack-command"
);
static
const
QLatin1String
UNPACK_COMMAND_OPENPGP_ENTRY
(
"unpack-command-openpgp"
);
static
const
QLatin1String
UNPACK_COMMAND_CMS_ENTRY
(
"unpack-command-cms"
);
static
const
QLatin1String
EXTENSIONS_ENTRY
(
"extensions"
);
static
const
QLatin1String
EXTENSIONS_OPENPGP_ENTRY
(
"extensions-openpgp"
);
static
const
QLatin1String
EXTENSIONS_CMS_ENTRY
(
"extensions-cms"
);
static
const
QLatin1String
FILE_PLACEHOLDER
(
"%f"
);
static
const
QLatin1String
INSTALLPATH_PLACEHOLDER
(
"%I"
);
static
const
QLatin1String
NULL_SEPARATED_STDIN_INDICATOR
(
"0|"
);
static
const
QLatin1Char
NEWLINE_SEPARATED_STDIN_INDICATOR
(
'|'
);
namespace
{
class
ArchiveDefinitionError
:
public
Kleo
::
Exception
{
const
QString
m_id
;
public
:
ArchiveDefinitionError
(
const
QString
&
id
,
const
QString
&
message
)
:
Kleo
::
Exception
(
GPG_ERR_INV_PARAMETER
,
i18n
(
"Error in archive definition %1: %2"
,
id
,
message
),
MessageOnly
),
m_id
(
id
)
{
}
~
ArchiveDefinitionError
()
throw
()
{}
const
QString
&
archiveDefinitionId
()
const
{
return
m_id
;
}
};
}
static
QString
try_extensions
(
const
QString
&
path
)
{
static
const
char
exts
[][
4
]
=
{
""
,
"exe"
,
"bat"
,
"bin"
,
"cmd"
,
};
static
const
size_t
numExts
=
sizeof
exts
/
sizeof
*
exts
;
for
(
unsigned
int
i
=
0
;
i
<
numExts
;
++
i
)
{
const
QFileInfo
fi
(
path
+
QLatin1Char
(
'.'
)
+
QLatin1String
(
exts
[
i
]));
if
(
fi
.
exists
())
{
return
fi
.
filePath
();
}
}
return
QString
();
}
static
void
parse_command
(
QString
cmdline
,
const
QString
&
id
,
const
QString
&
whichCommand
,
QString
*
command
,
QStringList
*
prefix
,
QStringList
*
suffix
,
ArchiveDefinition
::
ArgumentPassingMethod
*
method
,
bool
parseFilePlaceholder
)
{
assert
(
prefix
);
assert
(
suffix
);
assert
(
method
);
KShell
::
Errors
errors
;
QStringList
l
;
if
(
cmdline
.
startsWith
(
NULL_SEPARATED_STDIN_INDICATOR
))
{
*
method
=
ArchiveDefinition
::
NullSeparatedInputFile
;
cmdline
.
remove
(
0
,
2
);
}
else
if
(
cmdline
.
startsWith
(
NEWLINE_SEPARATED_STDIN_INDICATOR
))
{
*
method
=
ArchiveDefinition
::
NewlineSeparatedInputFile
;
cmdline
.
remove
(
0
,
1
);
}
else
{
*
method
=
ArchiveDefinition
::
CommandLine
;
}
if
(
*
method
!=
ArchiveDefinition
::
CommandLine
&&
cmdline
.
contains
(
FILE_PLACEHOLDER
))
{
throw
ArchiveDefinitionError
(
id
,
i18n
(
"Cannot use both %f and | in '%1'"
,
whichCommand
));
}
cmdline
.
replace
(
FILE_PLACEHOLDER
,
QLatin1String
(
"__files_go_here__"
))
.
replace
(
INSTALLPATH_PLACEHOLDER
,
QStringLiteral
(
"__path_goes_here__"
));
l
=
KShell
::
splitArgs
(
cmdline
,
KShell
::
AbortOnMeta
|
KShell
::
TildeExpand
,
&
errors
);
l
=
l
.
replaceInStrings
(
QStringLiteral
(
"__files_go_here__"
),
FILE_PLACEHOLDER
);
if
(
l
.
indexOf
(
QRegExp
(
QLatin1String
(
".*__path_goes_here__.*"
)))
>=
0
)
{
l
=
l
.
replaceInStrings
(
QStringLiteral
(
"__path_goes_here__"
),
ArchiveDefinition
::
installPath
());
}
if
(
errors
==
KShell
::
BadQuoting
)
{
throw
ArchiveDefinitionError
(
id
,
i18n
(
"Quoting error in '%1' entry"
,
whichCommand
));
}
if
(
errors
==
KShell
::
FoundMeta
)
{
throw
ArchiveDefinitionError
(
id
,
i18n
(
"'%1' too complex (would need shell)", whichCommand))
;
}
qCDebug
(
KLEOPATRA_LOG
)
<<
"ArchiveDefinition["
<<
id
<<
']'
<<
l
;
if
(
l
.
empty
())
{
throw
ArchiveDefinitionError
(
id
,
i18n
(
"'%1' entry is empty/missing"
,
whichCommand
));
}
const
QFileInfo
fi1
(
l
.
front
());
if
(
fi1
.
isAbsolute
())
{
*
command
=
try_extensions
(
l
.
front
());
}
else
{
*
command
=
QStandardPaths
::
findExecutable
(
fi1
.
fileName
());
}
if
(
command
->
isEmpty
())
{
throw
ArchiveDefinitionError
(
id
,
i18n
(
"'%1' empty or not found"
,
whichCommand
));
}
if
(
parseFilePlaceholder
)
{
const
int
idx1
=
l
.
indexOf
(
FILE_PLACEHOLDER
);
if
(
idx1
<
0
)
{
// none -> append
*
prefix
=
l
.
mid
(
1
);
}
else
{
*
prefix
=
l
.
mid
(
1
,
idx1
-
1
);
*
suffix
=
l
.
mid
(
idx1
+
1
);
}
}
else
{
*
prefix
=
l
.
mid
(
1
);
}
switch
(
*
method
)
{
case
ArchiveDefinition
::
CommandLine
:
qCDebug
(
KLEOPATRA_LOG
)
<<
"ArchiveDefinition["
<<
id
<<
']'
<<
*
command
<<
*
prefix
<<
FILE_PLACEHOLDER
<<
*
suffix
;
break
;
case
ArchiveDefinition
::
NewlineSeparatedInputFile
:
qCDebug
(
KLEOPATRA_LOG
)
<<
"ArchiveDefinition["
<<
id
<<
']'
<<
"find | "
<<
*
command
<<
*
prefix
;
break
;
case
ArchiveDefinition
::
NullSeparatedInputFile
:
qCDebug
(
KLEOPATRA_LOG
)
<<
"ArchiveDefinition["
<<
id
<<
']'
<<
"find -print0 | "
<<
*
command
<<
*
prefix
;
break
;
case
ArchiveDefinition
::
NumArgumentPassingMethods
:
assert
(
!
"Should not happen"
);
break
;
}
}
namespace
{
class
KConfigBasedArchiveDefinition
:
public
ArchiveDefinition
{
public
:
explicit
KConfigBasedArchiveDefinition
(
const
KConfigGroup
&
group
)
:
ArchiveDefinition
(
group
.
readEntryUntranslated
(
ID_ENTRY
),
group
.
readEntry
(
NAME_ENTRY
))
{
if
(
id
().
isEmpty
())
{
throw
ArchiveDefinitionError
(
group
.
name
(),
i18n
(
"'%1' entry is empty/missing"
,
ID_ENTRY
));
}
QStringList
extensions
;
QString
extensionsKey
;
// extensions(-openpgp)
if
(
group
.
hasKey
(
EXTENSIONS_OPENPGP_ENTRY
))
{
extensionsKey
=
EXTENSIONS_OPENPGP_ENTRY
;
}
else
{
extensionsKey
=
EXTENSIONS_ENTRY
;
}
extensions
=
group
.
readEntry
(
extensionsKey
,
QStringList
());
if
(
extensions
.
empty
())
{
throw
ArchiveDefinitionError
(
id
(),
i18n
(
"'%1' entry is empty/missing"
,
extensionsKey
));
}
setExtensions
(
OpenPGP
,
extensions
);
// extensions(-cms)
if
(
group
.
hasKey
(
EXTENSIONS_CMS_ENTRY
))
{
extensionsKey
=
EXTENSIONS_CMS_ENTRY
;
}
else
{
extensionsKey
=
EXTENSIONS_ENTRY
;
}
extensions
=
group
.
readEntry
(
extensionsKey
,
QStringList
());
if
(
extensions
.
empty
())
{
throw
ArchiveDefinitionError
(
id
(),
i18n
(
"'%1' entry is empty/missing"
,
extensionsKey
));
}
setExtensions
(
CMS
,
extensions
);
ArgumentPassingMethod
method
;
// pack-command(-openpgp)
if
(
group
.
hasKey
(
PACK_COMMAND_OPENPGP_ENTRY
))
parse_command
(
group
.
readEntry
(
PACK_COMMAND_OPENPGP_ENTRY
),
id
(),
PACK_COMMAND_OPENPGP_ENTRY
,
&
m_packCommand
[
OpenPGP
],
&
m_packPrefixArguments
[
OpenPGP
],
&
m_packPostfixArguments
[
OpenPGP
],
&
method
,
true
);
else
parse_command
(
group
.
readEntry
(
PACK_COMMAND_ENTRY
),
id
(),
PACK_COMMAND_ENTRY
,
&
m_packCommand
[
OpenPGP
],
&
m_packPrefixArguments
[
OpenPGP
],
&
m_packPostfixArguments
[
OpenPGP
],
&
method
,
true
);
setPackCommandArgumentPassingMethod
(
OpenPGP
,
method
);
// pack-command(-cms)
if
(
group
.
hasKey
(
PACK_COMMAND_CMS_ENTRY
))
parse_command
(
group
.
readEntry
(
PACK_COMMAND_CMS_ENTRY
),
id
(),
PACK_COMMAND_CMS_ENTRY
,
&
m_packCommand
[
CMS
],
&
m_packPrefixArguments
[
CMS
],
&
m_packPostfixArguments
[
CMS
],
&
method
,
true
);
else
parse_command
(
group
.
readEntry
(
PACK_COMMAND_ENTRY
),
id
(),
PACK_COMMAND_ENTRY
,
&
m_packCommand
[
CMS
],
&
m_packPrefixArguments
[
CMS
],
&
m_packPostfixArguments
[
CMS
],
&
method
,
true
);
setPackCommandArgumentPassingMethod
(
CMS
,
method
);
QStringList
dummy
;
// unpack-command(-openpgp)
if
(
group
.
hasKey
(
UNPACK_COMMAND_OPENPGP_ENTRY
))
parse_command
(
group
.
readEntry
(
UNPACK_COMMAND_OPENPGP_ENTRY
),
id
(),
UNPACK_COMMAND_OPENPGP_ENTRY
,
&
m_unpackCommand
[
OpenPGP
],
&
m_unpackArguments
[
OpenPGP
],
&
dummy
,
&
method
,
false
);
else
parse_command
(
group
.
readEntry
(
UNPACK_COMMAND_ENTRY
),
id
(),
UNPACK_COMMAND_ENTRY
,
&
m_unpackCommand
[
OpenPGP
],
&
m_unpackArguments
[
OpenPGP
],
&
dummy
,
&
method
,
false
);
if
(
method
!=
CommandLine
)
{
throw
ArchiveDefinitionError
(
id
(),
i18n
(
"cannot use argument passing on standard input for unpack-command"
));
}
// unpack-command(-cms)
if
(
group
.
hasKey
(
UNPACK_COMMAND_CMS_ENTRY
))
parse_command
(
group
.
readEntry
(
UNPACK_COMMAND_CMS_ENTRY
),
id
(),
UNPACK_COMMAND_CMS_ENTRY
,
&
m_unpackCommand
[
CMS
],
&
m_unpackArguments
[
CMS
],
&
dummy
,
&
method
,
false
);
else
parse_command
(
group
.
readEntry
(
UNPACK_COMMAND_ENTRY
),
id
(),
UNPACK_COMMAND_ENTRY
,
&
m_unpackCommand
[
CMS
],
&
m_unpackArguments
[
CMS
],
&
dummy
,
&
method
,
false
);
if
(
method
!=
CommandLine
)
{
throw
ArchiveDefinitionError
(
id
(),
i18n
(
"cannot use argument passing on standard input for unpack-command"
));
}
}
private
:
QString
doGetPackCommand
(
GpgME
::
Protocol
p
)
const
Q_DECL_OVERRIDE
{
return
m_packCommand
[
p
];
}
QString
doGetUnpackCommand
(
GpgME
::
Protocol
p
)
const
Q_DECL_OVERRIDE
{
return
m_unpackCommand
[
p
];
}
QStringList
doGetPackArguments
(
GpgME
::
Protocol
p
,
const
QStringList
&
files
)
const
Q_DECL_OVERRIDE
{
return
m_packPrefixArguments
[
p
]
+
files
+
m_packPostfixArguments
[
p
];
}
QStringList
doGetUnpackArguments
(
GpgME
::
Protocol
p
,
const
QString
&
file
)
const
Q_DECL_OVERRIDE
{
QStringList
copy
=
m_unpackArguments
[
p
];
copy
.
replaceInStrings
(
FILE_PLACEHOLDER
,
file
);
return
copy
;
}
private
:
QString
m_packCommand
[
2
],
m_unpackCommand
[
2
];
QStringList
m_packPrefixArguments
[
2
],
m_packPostfixArguments
[
2
];
QStringList
m_unpackArguments
[
2
];
};
}
ArchiveDefinition
::
ArchiveDefinition
(
const
QString
&
id
,
const
QString
&
label
)
:
m_id
(
id
),
m_label
(
label
)
{
m_packCommandMethod
[
GpgME
::
OpenPGP
]
=
m_packCommandMethod
[
GpgME
::
CMS
]
=
CommandLine
;
}
ArchiveDefinition
::~
ArchiveDefinition
()
{}
static
QByteArray
make_input
(
const
QStringList
&
files
,
char
sep
)
{
QByteArray
result
;
Q_FOREACH
(
const
QString
&
file
,
files
)
{
result
+=
QFile
::
encodeName
(
file
);
result
+=
sep
;
}
return
result
;
}
std
::
shared_ptr
<
Input
>
ArchiveDefinition
::
createInputFromPackCommand
(
GpgME
::
Protocol
p
,
const
QStringList
&
files
)
const
{
checkProtocol
(
p
);
const
QString
base
=
heuristicBaseDirectory
(
files
);
if
(
base
.
isEmpty
())
{
throw
Kleo
::
Exception
(
GPG_ERR_CONFLICT
,
i18n
(
"Cannot find common base directory for these files:
\n
%1"
,
files
.
join
(
QLatin1Char
(
'\n'
))));
}
qCDebug
(
KLEOPATRA_LOG
)
<<
"heuristicBaseDirectory("
<<
files
<<
") ->"
<<
base
;
const
QStringList
relative
=
makeRelativeTo
(
base
,
files
);
qCDebug
(
KLEOPATRA_LOG
)
<<
"relative"
<<
relative
;
switch
(
m_packCommandMethod
[
p
])
{
case
CommandLine
:
return
Input
::
createFromProcessStdOut
(
doGetPackCommand
(
p
),
doGetPackArguments
(
p
,
relative
),
QDir
(
base
));
case
NewlineSeparatedInputFile
:
return
Input
::
createFromProcessStdOut
(
doGetPackCommand
(
p
),
doGetPackArguments
(
p
,
QStringList
()),
QDir
(
base
),
make_input
(
relative
,
'\n'
));
case
NullSeparatedInputFile
:
return
Input
::
createFromProcessStdOut
(
doGetPackCommand
(
p
),
doGetPackArguments
(
p
,
QStringList
()),
QDir
(
base
),
make_input
(
relative
,
'\0'
));
case
NumArgumentPassingMethods
:
assert
(
!
"Should not happen"
);
}
return
std
::
shared_ptr
<
Input
>
();
// make compiler happy
}
std
::
shared_ptr
<
Output
>
ArchiveDefinition
::
createOutputFromUnpackCommand
(
GpgME
::
Protocol
p
,
const
QString
&
file
,
const
QDir
&
wd
)
const
{
checkProtocol
(
p
);
const
QFileInfo
fi
(
file
);
return
Output
::
createFromProcessStdIn
(
doGetUnpackCommand
(
p
),
doGetUnpackArguments
(
p
,
fi
.
absoluteFilePath
()),
wd
);
}
// static
std
::
vector
<
std
::
shared_ptr
<
ArchiveDefinition
>
>
ArchiveDefinition
::
getArchiveDefinitions
()
{
QStringList
errors
;
return
getArchiveDefinitions
(
errors
);
}
// static
std
::
vector
<
std
::
shared_ptr
<
ArchiveDefinition
>
>
ArchiveDefinition
::
getArchiveDefinitions
(
QStringList
&
errors
)
{
std
::
vector
<
std
::
shared_ptr
<
ArchiveDefinition
>
>
result
;
KSharedConfigPtr
config
=
KSharedConfig
::
openConfig
(
QStringLiteral
(
"libkleopatrarc"
));
const
QStringList
groups
=
config
->
groupList
().
filter
(
QRegularExpression
(
QStringLiteral
(
"^Archive Definition #"
)));
result
.
reserve
(
groups
.
size
());
Q_FOREACH
(
const
QString
&
group
,
groups
)
try
{
const
std
::
shared_ptr
<
ArchiveDefinition
>
ad
(
new
KConfigBasedArchiveDefinition
(
KConfigGroup
(
config
,
group
)));
result
.
push_back
(
ad
);
}
catch
(
const
std
::
exception
&
e
)
{
qCDebug
(
KLEOPATRA_LOG
)
<<
e
.
what
();
errors
.
push_back
(
QString
::
fromLocal8Bit
(
e
.
what
()));
}
catch
(...)
{
errors
.
push_back
(
i18n
(
"Caught unknown exception in group %1"
,
group
));
}
return
result
;
}
void
ArchiveDefinition
::
checkProtocol
(
GpgME
::
Protocol
p
)
const
{
kleo_assert
(
p
==
GpgME
::
OpenPGP
||
p
==
GpgME
::
CMS
);
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Thu, Mar 19, 6:03 PM (15 h, 34 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ad/a7/1fed0f05e7b4992003a173968385
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment