1 | /* |
2 | SPDX-FileCopyrightText: 2008 Nicola Gigante <nicola.gigante@gmail.com> |
3 | SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.1-or-later |
6 | */ |
7 | |
8 | #include "policy-gen.h" |
9 | |
10 | #include <QCoreApplication> |
11 | #include <QDebug> |
12 | #include <QFile> |
13 | #include <QRegularExpression> |
14 | #include <QSettings> |
15 | #include <QStringList> |
16 | |
17 | #include <cerrno> |
18 | #include <cstdio> |
19 | |
20 | using namespace std; |
21 | |
22 | QList<Action> parse(QSettings &ini); |
23 | QMap<QString, QString> parseDomain(const QSettings &ini); |
24 | |
25 | int main(int argc, char **argv) |
26 | { |
27 | QCoreApplication app(argc, argv); |
28 | |
29 | if (argc < 2) { |
30 | qCritical(msg: "Too few arguments" ); |
31 | return 1; |
32 | } |
33 | |
34 | QSettings ini(QFile::decodeName(localFileName: argv[1]), QSettings::IniFormat); |
35 | if (ini.status()) { |
36 | qCritical(msg: "Error loading file: %s" , argv[1]); |
37 | return 1; |
38 | } |
39 | |
40 | if (argc == 3) { |
41 | // Support an optional 2nd argument pointing to the output file |
42 | // |
43 | // This is safer to use in build systems than |
44 | // "kauth-policy-gen foo.actions > foo.policy" because when using a |
45 | // redirection "foo.policy" is created even if kauth-policy-gen fails. |
46 | // This means the first call to make fails, but a second call succeeds |
47 | // because an empty "foo.policy" exists. |
48 | if (!freopen(filename: argv[2], modes: "w" , stdout)) { |
49 | qCritical(msg: "Failed to open %s for writing: %s" , argv[2], strerror(errno)); |
50 | return 1; |
51 | } |
52 | } |
53 | |
54 | output(actions: parse(ini), domain: parseDomain(ini)); |
55 | } |
56 | |
57 | QList<Action> parse(QSettings &ini) |
58 | { |
59 | // clazy:excludeall=use-static-qregularexpression |
60 | // example: [org.kde.kcontrol.kcmfoo.save] |
61 | const QRegularExpression actionExp(QRegularExpression::anchoredPattern(QStringLiteral("[0-9a-z]+(\\.[0-9a-z]+)*" ))); |
62 | // example: Description[ca]=Mòdul de control del Foo. |
63 | const QRegularExpression descriptionExp(QRegularExpression::anchoredPattern(QStringLiteral("description(?:\\[(\\w+)\\])?" )), |
64 | QRegularExpression::CaseInsensitiveOption); |
65 | // example: Name[ca]=Mòdul de control del Foo |
66 | const QRegularExpression nameExp(QRegularExpression::anchoredPattern(QStringLiteral("name(?:\\[(\\w+)\\])?" )), QRegularExpression::CaseInsensitiveOption); |
67 | // example: Policy=auth_admin |
68 | const QRegularExpression policyExp(QRegularExpression::anchoredPattern(QStringLiteral("(?:yes|no|auth_self|auth_admin)" ))); |
69 | |
70 | QList<Action> actions; |
71 | const auto listChilds = ini.childGroups(); |
72 | for (const QString &name : listChilds) { |
73 | Action action; |
74 | |
75 | if (name == QLatin1String("Domain" )) { |
76 | continue; |
77 | } |
78 | |
79 | if (!actionExp.match(subject: name).hasMatch()) { |
80 | qCritical(msg: "Wrong action syntax: %s\n" , name.toLatin1().data()); |
81 | exit(status: 1); |
82 | } |
83 | |
84 | action.name = name; |
85 | ini.beginGroup(prefix: name); |
86 | |
87 | const QStringList listChildKeys = ini.childKeys(); |
88 | for (const QString &key : listChildKeys) { |
89 | QRegularExpressionMatch match; |
90 | if ((match = descriptionExp.match(subject: key)).hasMatch()) { |
91 | QString lang = match.captured(nth: 1); |
92 | |
93 | if (lang.isEmpty()) { |
94 | lang = QString::fromLatin1(ba: "en" ); |
95 | } |
96 | |
97 | action.descriptions.insert(key: lang, value: ini.value(key).toString()); |
98 | |
99 | } else if ((match = nameExp.match(subject: key)).hasMatch()) { |
100 | QString lang = match.captured(nth: 1); |
101 | |
102 | if (lang.isEmpty()) { |
103 | lang = QString::fromLatin1(ba: "en" ); |
104 | } |
105 | |
106 | action.messages.insert(key: lang, value: ini.value(key).toString()); |
107 | |
108 | } else if (key.toLower() == QLatin1String("policy" )) { |
109 | QString policy = ini.value(key).toString(); |
110 | if (!policyExp.match(subject: policy).hasMatch()) { |
111 | qCritical(msg: "Wrong policy: %s" , policy.toLatin1().data()); |
112 | exit(status: 1); |
113 | } |
114 | action.policy = policy; |
115 | |
116 | } else if (key.toLower() == QLatin1String("policyinactive" )) { |
117 | QString policyInactive = ini.value(key).toString(); |
118 | if (!policyExp.match(subject: policyInactive).hasMatch()) { |
119 | qCritical(msg: "Wrong policy: %s" , policyInactive.toLatin1().data()); |
120 | exit(status: 1); |
121 | } |
122 | action.policyInactive = policyInactive; |
123 | |
124 | } else if (key.toLower() == QLatin1String("persistence" )) { |
125 | QString persistence = ini.value(key).toString(); |
126 | if (persistence != QLatin1String("session" ) && persistence != QLatin1String("always" )) { |
127 | qCritical(msg: "Wrong persistence: %s" , persistence.toLatin1().data()); |
128 | exit(status: 1); |
129 | } |
130 | action.persistence = persistence; |
131 | } |
132 | } |
133 | |
134 | if (action.policy.isEmpty() || action.messages.isEmpty() || action.descriptions.isEmpty()) { |
135 | qCritical(msg: "Missing option in action: %s" , name.toLatin1().data()); |
136 | exit(status: 1); |
137 | } |
138 | ini.endGroup(); |
139 | |
140 | actions.append(t: action); |
141 | } |
142 | |
143 | return actions; |
144 | } |
145 | |
146 | QMap<QString, QString> parseDomain(const QSettings &ini) |
147 | { |
148 | QMap<QString, QString> rethash; |
149 | |
150 | if (ini.childGroups().contains(str: QString::fromLatin1(ba: "Domain" ))) { |
151 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/Name" ))) { |
152 | rethash[QString::fromLatin1(ba: "vendor" )] = ini.value(key: QString::fromLatin1(ba: "Domain/Name" )).toString(); |
153 | } |
154 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/URL" ))) { |
155 | rethash[QString::fromLatin1(ba: "vendorurl" )] = ini.value(key: QString::fromLatin1(ba: "Domain/URL" )).toString(); |
156 | } |
157 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/Icon" ))) { |
158 | rethash[QString::fromLatin1(ba: "icon" )] = ini.value(key: QString::fromLatin1(ba: "Domain/Icon" )).toString(); |
159 | } |
160 | } |
161 | |
162 | return rethash; |
163 | } |
164 | |