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 | QList<Action> actions; |
60 | |
61 | // example: [org.kde.kcontrol.kcmfoo.save] |
62 | const QRegularExpression actionExp(QRegularExpression::anchoredPattern(QStringLiteral("[0-9a-z]+(\\.[0-9a-z]+)*" ))); |
63 | |
64 | // example: Description[ca]=Mòdul de control del Foo. |
65 | const QRegularExpression descriptionExp(QRegularExpression::anchoredPattern(QStringLiteral("description(?:\\[(\\w+)\\])?" )), |
66 | QRegularExpression::CaseInsensitiveOption); |
67 | |
68 | // example: Name[ca]=Mòdul de control del Foo |
69 | const QRegularExpression nameExp(QRegularExpression::anchoredPattern(QStringLiteral("name(?:\\[(\\w+)\\])?" )), QRegularExpression::CaseInsensitiveOption); |
70 | |
71 | // example: Policy=auth_admin |
72 | const QRegularExpression policyExp(QRegularExpression::anchoredPattern(QStringLiteral("(?:yes|no|auth_self|auth_admin)" ))); |
73 | |
74 | const auto listChilds = ini.childGroups(); |
75 | for (const QString &name : listChilds) { |
76 | Action action; |
77 | |
78 | if (name == QLatin1String("Domain" )) { |
79 | continue; |
80 | } |
81 | |
82 | if (!actionExp.match(subject: name).hasMatch()) { |
83 | qCritical(msg: "Wrong action syntax: %s\n" , name.toLatin1().data()); |
84 | exit(status: 1); |
85 | } |
86 | |
87 | action.name = name; |
88 | ini.beginGroup(prefix: name); |
89 | |
90 | const auto listChildKeys = ini.childKeys(); |
91 | for (const QString &key : listChildKeys) { |
92 | QRegularExpressionMatch match; |
93 | if ((match = descriptionExp.match(subject: key)).hasMatch()) { |
94 | QString lang = match.captured(nth: 1); |
95 | |
96 | if (lang.isEmpty()) { |
97 | lang = QString::fromLatin1(ba: "en" ); |
98 | } |
99 | |
100 | action.descriptions.insert(key: lang, value: ini.value(key).toString()); |
101 | |
102 | } else if ((match = nameExp.match(subject: key)).hasMatch()) { |
103 | QString lang = match.captured(nth: 1); |
104 | |
105 | if (lang.isEmpty()) { |
106 | lang = QString::fromLatin1(ba: "en" ); |
107 | } |
108 | |
109 | action.messages.insert(key: lang, value: ini.value(key).toString()); |
110 | |
111 | } else if (key.toLower() == QLatin1String("policy" )) { |
112 | QString policy = ini.value(key).toString(); |
113 | if (!policyExp.match(subject: policy).hasMatch()) { |
114 | qCritical(msg: "Wrong policy: %s" , policy.toLatin1().data()); |
115 | exit(status: 1); |
116 | } |
117 | action.policy = policy; |
118 | |
119 | } else if (key.toLower() == QLatin1String("policyinactive" )) { |
120 | QString policyInactive = ini.value(key).toString(); |
121 | if (!policyExp.match(subject: policyInactive).hasMatch()) { |
122 | qCritical(msg: "Wrong policy: %s" , policyInactive.toLatin1().data()); |
123 | exit(status: 1); |
124 | } |
125 | action.policyInactive = policyInactive; |
126 | |
127 | } else if (key.toLower() == QLatin1String("persistence" )) { |
128 | QString persistence = ini.value(key).toString(); |
129 | if (persistence != QLatin1String("session" ) && persistence != QLatin1String("always" )) { |
130 | qCritical(msg: "Wrong persistence: %s" , persistence.toLatin1().data()); |
131 | exit(status: 1); |
132 | } |
133 | action.persistence = persistence; |
134 | } |
135 | } |
136 | |
137 | if (action.policy.isEmpty() || action.messages.isEmpty() || action.descriptions.isEmpty()) { |
138 | qCritical(msg: "Missing option in action: %s" , name.toLatin1().data()); |
139 | exit(status: 1); |
140 | } |
141 | ini.endGroup(); |
142 | |
143 | actions.append(t: action); |
144 | } |
145 | |
146 | return actions; |
147 | } |
148 | |
149 | QMap<QString, QString> parseDomain(const QSettings &ini) |
150 | { |
151 | QMap<QString, QString> rethash; |
152 | |
153 | if (ini.childGroups().contains(str: QString::fromLatin1(ba: "Domain" ))) { |
154 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/Name" ))) { |
155 | rethash[QString::fromLatin1(ba: "vendor" )] = ini.value(key: QString::fromLatin1(ba: "Domain/Name" )).toString(); |
156 | } |
157 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/URL" ))) { |
158 | rethash[QString::fromLatin1(ba: "vendorurl" )] = ini.value(key: QString::fromLatin1(ba: "Domain/URL" )).toString(); |
159 | } |
160 | if (ini.contains(key: QString::fromLatin1(ba: "Domain/Icon" ))) { |
161 | rethash[QString::fromLatin1(ba: "icon" )] = ini.value(key: QString::fromLatin1(ba: "Domain/Icon" )).toString(); |
162 | } |
163 | } |
164 | |
165 | return rethash; |
166 | } |
167 | |