1/*
2* Copyright (C) 2008 Nicola Gigante <nicola.gigante@gmail.com>
3* Copyright (C) 2009 Dario Freddi <drf@kde.org>
4*
5* This program is free software; you can redistribute it and/or modify
6* it under the terms of the GNU Lesser General Public License as published by
7* the Free Software Foundation; either version 2.1 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU Lesser General Public License
16* along with this program; if not, write to the
17* Free Software Foundation, Inc.,
18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .
19*/
20
21#ifndef ACTION_REPLY_H
22#define ACTION_REPLY_H
23
24#include <QtCore/QString>
25#include <QtCore/QVariant>
26#include <QtCore/QMap>
27#include <QtCore/QDataStream>
28
29#include <kdecore_export.h>
30/**
31 @namespace KAuth
32
33 @section kauth_intro Introduction
34 The KDE Authorization API allows developers to write desktop applications that run high-privileged tasks in an easy, secure and cross-platform way. Previously, if an application had to do administrative tasks,
35 it had to be run as root, using mechanisms such as sudo or graphical equivalents, or by setting the executable's setuid bit. This approach has some drawbacks. For example, the whole application code,
36 including GUI handling and network communication, had to be done as root. More code that runs as root means more possible security holes.
37
38 The solution is the caller/helper pattern. With this pattern, the privileged code is isolated in a small helper tool that runs as root. This tool includes only the few lines of code that actually
39 need to be run with privileges, not the whole application logic. All the other parts of the application are run as a normal user, and the helper tool is called when needed, using a secure mechanism that
40 ensures that the user is authorized to do so. This pattern is not very easy to implement, because the developer has to deal with a lot of details about how to authorize the user, how to call the helper
41 with the right privileges, how to exchange data with the helper, etc.. This is where the new KDE Authorization API becomes useful. Thanks to this new library, every developer can implement the
42 caller/helper pattern to write application that require high privileges, with a few lines of code in an easy, secure and cross-platform way.
43
44 Not only: the library can also be used to lock down some actions in your application without using a helper but just checking for authorization and verifying if the user is allowed to perform it.
45
46 The KDE Authorization library uses different backends depending on the system where it's built. As far as the user authorization is concerned, it currently uses PolicyKit on linux and Authorization Services
47 on Mac OSX, and a Windows backend will eventually be written, too. At the communication layer, the library uses D-Bus on every supported platform.
48
49
50 @section kauth_concepts Concepts
51 There are a few concepts to understand when using the library. Much of those are carried from underlying APIs such as PolicyKit, so if you know something about them there shouldn't be problems.
52
53 An <i>action</i> is a single task that needs to be done by the application. You refer to an action using an action identifier, which is a string in reverse domain name syntax (to avoid duplicates). For
54 example, if the date/time control center module needs to change the date, it would need an action like "org.kde.datatime.change". If your application has to perform more than one privileged task, you
55 should configure more than one action. This allows system administrators to fine tune the policies that allow users to perform your actions.
56
57 The <i>authorization</i> is the process that is executed to decide if a user can perform an action or not. In order to execute the helper as root, the user has to be authorized. For example, on linux,
58 che policykit backend will look at the policykit policy database to see what requirements the user has to meet in order to execute the action you requested. The policy set for that action could allow
59 or deny that user, or could say the user has to authenticate in order to gain the authorization.
60
61 The <i>authentication</i> is the process that allows the system to know that the person is in front of the console is who he says to be. If an action can be allowed or not depending on the user's identity,
62 it has to be proved by entering a password or any other identification data the system requires.
63
64 A typical session with the authorization API is like this:
65 - The user want to perform some privileged task
66 - The application asks the system if the user is authorized.
67 - The system asks the user to authenticate, if needed, and reply the application.
68 - The application uses some system-provided mechanism to execute the helper's code as the root user. Previously, you had to set the setuid bit to do this, but we have something cool called
69 "dbus activation" that doesn't require the setuid bit and is much more flexible.
70 - The helper code, immediately after starting, checks if the caller is authorized to do what it asks. If not the helper immediately exits!
71 - If the caller is authorized, the helper executes the task and exits.
72 - The application receives data back from the helper.
73
74 All these steps are managed by the library. Following sections will focus on how to write the helper to implement your actions and how to call the helper from the application.
75
76 @section kauth_helper Writing the helper tool
77
78 The first thing you need to do before writing anything is to decide what actions you need to implement. Every action needs to be identified by a string in the reverse domain name syntax. This helps to
79 avoid duplicates. An example of action id is "org.kde.datetime.change" or "org.kde.ksysguard.killprocess". Action names can only contain lowercase letters and dots (not as the first or last char).
80 You also need an identifier for your helper. An application using the KDE auth api can implement and use more than one helper, implementing different actions. An helper is uniquely identified in the
81 system context with a string. It, again, is in reverse domain name syntax to avoid duplicates. A common approach is to call the helper like the common prefix of your action names.
82 For example, the Date/Time kcm module could use a helper called "org.kde.datetime", to perform actions like "org.kde.datetime.changedate" and "org.kde.datetime.changetime". This naming convention
83 simplifies the implementation of the helper.
84
85 From the code point of view, the helper is implemented as a QObject subclass. Every action is implemented by a public slot. In the example/ directory in the source code tree you find a complete example.
86 Let's look at that.
87 The helper.h file declares the class that implements the helper. It looks like:
88
89 @code
90 #include <kauth.h>
91
92 using namespace KAuth;
93
94 class MyHelper : public QObject
95 {
96 Q_OBJECT
97
98 public slots:
99 ActionReply read(const QVariantMap& args);
100 ActionReply write(const QVariantMap& args);
101 ActionReply longaction(const QVariantMap& args);
102 };
103 @endcode
104
105 The slot names are the last part of the action name, without the helper's ID if it's a prefix, with all the dots replaced by underscores. In this case, the helper ID is "org.kde.auth.example", so those
106 three slots implement the actions "org.kde.auth.example.read", "org.kde.auth.example.write" and "org.kde.auth.example.longaction". The helper ID doesn't have to appear at the beginning of the action
107 name, but it's good practice. If you want to extend MyHelper to implement also a different action like "org.kde.datetime.changetime", since the helper ID doesn't match you'll have to implement a
108 slot called org_kde_datetime_changetime().
109
110 The slot's signature is fixed: the return type is ActionReply, a class that allows you to return results, error codes and custom data to the application when your action has finished to run.
111 Please note that due to QMetaObject being picky about namespaces, you NEED to declare the return type as ActionReply and not KAuth::ActionReply. So the using declaration is compulsory
112 The QVariantMap object that comes as argument contains custom data coming from the application.
113
114 Let's look at the read action implementation. Its purpose is to read files:
115
116 @code
117 ActionReply MyHelper::read(QVariantMap args)
118 {
119 ActionReply reply;
120 QString filename = args["filename"].toString();
121 QFile file(filename);
122 if (!file.open(QIODevice::ReadOnly)) {
123 reply = ActionReply::HelperErrorReply;
124 reply.setErrorCode(file.error());
125
126 return reply;
127 }
128
129 QTextStream stream(&file);
130 QString contents;
131 stream >> contents;
132 reply.data()["contents"] = contents;
133
134 return reply;
135 }
136 @endcode
137
138 First, the code creates a default reply object. The default constructor creates a reply that reports success. Then it gets the filename parameter from the argument QVariantMap, that has previously been
139 set by the application, before calling the helper. If it fails to open the file, it creates an ActionReply object that notifies that some error has happened in the helper, then set the error code to
140 that returned by QFile and returns. If there is no error, it reads the file. The contents are put into the reply.data() object, which is another QVariantMap. It will be sent back to the application
141 with the reply.
142
143 Because this class will be compiled into a standalone executable, we need a main() function and some code to initialize everything: you don't have to write it. Instead, you use the
144 KDE4_AUTH_HELPER_MAIN() macro that will take care of everything. It's used like this:
145
146 @code
147 KDE4_AUTH_HELPER_MAIN("org.kde.auth.example", MyHelper)
148 @endcode
149
150 The first parameter is the string containing the helper identifier. Please note that you need to use this same string in the application's code to tell the library which helper to call, so please
151 stay away from typos, because we don't have any way to detect them. The second parameter is the name of the helper's class.
152 Your helper, if complex, can be composed of a lot of source files, but the important thing is to include this macro in one at least one of them.
153
154 To build the helper, KDE macros provide a function named kde4_install_auth_helper_files(). Use it in your cmake file like this:
155
156 @code
157 kde4_add_executable(<helper_target> your sources...)
158 target_link_libraries(<helper_target> your libraries...)
159 install(TARGETS <helper_target> DESTINATION ${LIBEXEC_INSTALL_DIR})
160
161 kde4_install_auth_helper_files(<helper_target> <helper_id> <user>)
162 @endcode
163
164 The first argument is the cmake target name for the helper executable, which you have to build and install separately. Make sure to INSTALL THE HELPER IN ${LIBEXEC_INSTALL_DIR},
165 otherwise kde4_install_auth_helper_files will not work. The second argument is the
166 helper id. Please be sure to don't misspell it, and to not quote it. The user parameter is the user that the helper has to be run as. It usually is root, but some actions could require less strict
167 permissions, so you should use the right user where possible (for example the user apache if you have to mess with apache settings). Note that the target created by this macro already links to
168 libkauth and QtCore
169
170 @section kauth_actions Action registration
171 To be able to authorize the actions, they have to be added to the policy database. To do this in a cross-platform way, we provide a cmake macro. It looks like:
172 @code
173 kde4_install_auth_actions(<helper_id> <actions definition file>)
174 @endcode
175
176 The action definition file describes which actions are implemented by your code and which default security options they should have. It is a common text file in ini format, with one section for
177 each action and some parameters. The definition for the read action is:
178
179 @verbatim
180 [org.kde.auth.example.read]
181 Name=Read action
182 Description=Read action description
183 Policy=auth_admin
184 Persistence=session
185 @endverbatim
186
187 The name parameter is a text describing the action for <i>who reads the file</i>. The description parameter is the message shown to the user in the authentication dialog. It should be a finite phrase.
188 The policy attribute specify the default rule that the user must satisfy to be authorized. Possible values are:
189 - yes: the action should be always allowed
190 - no: the action should be always denied
191 - auth_self: the user should authenticate as itself
192 - auth_admin: the user should authenticate as an administrator user
193
194 The persistence attribute is optional. It says how long an authorization should be retained for that action. The values could be:
195 - session: the authorization persists until the user logs-out
196 - always: the authorization will persist indefinitely
197
198 @section kauth_app Calling the helper from the application
199
200 Once the helper is ready, we need to call it from the main application. In the example's mainwindow.cpp you can see how this is done. To create a reference to an action, an object of type Action has to
201 be created. Every Action object refers to an action by its action id. Two objects with the same action id will act on the same action.
202 With an Action object, you can authorize and execute the action. To execute an action you have a couple of choices:
203 - A synchronous call, using the Action::execute() method, will start the helper, execute the action and return the reply.
204 - An asynchronous call, by setting Action::setExecutesAsync(true) and calling ::execute(), will start the helper and return immediately.
205
206 The asynchronous call is the most flexible alternative, but you need a way to obtain the reply. This is done by connecting to a signal, but the Action class is not a QObject subclass. Instead, you connect
207 to signals exposed by the ActionWatcher class. For every action id you use, there is one ActionWatcher object. You can retrieve it using the Action::watcher() method. If you execute an action using
208 Action::executeAsync(), you can connect to the actionPerformed(ActionReply) signal to be notified when the action has been completed (or failed). As a parameter, you'll get a reply object containing
209 all the data you need. As a convenience, you can also pass an object and a slot to the executeAsync() method to directly connect them to the signal, if you don't want to mess with watchers.
210
211 The piece of code that calls the action of the previous example is located in example/mainwindow.cpp in the on_readAction_triggered() slot. It looks like this:
212 @code
213 QVariantMap args;
214 args["filename"] = filename;
215 Action readAction = "org.kde.auth.example.read";
216 readAction.setHelperID("org.kde.auth.example");
217 readAction.setArguments(args);
218
219 ActionReply reply = readAction.execute();
220 if (reply.failed())
221 QMessageBox::information(this, "Error", QString("KAuth returned an error code: %1").arg(reply.errorCode()));
222 else
223 contents = reply.data()["contents"].toString();
224 @endcode
225
226 First of all, it creates the action object specifying the action id. Then it loads the filename (we want to read a forbidden file) into the arguments() QVariantMap, which will be directly passed to the
227 helper in the read() slot's parameter. This example code uses a synchronous call to execute the action and retrieve the reply. If the reply succeeded, the reply data is retrieved from the returned QVariantMap
228 object. Please note that, although the execute() method will return only when the action is completed, the GUI will remain responsive because an internal event loop is entered. This means you should be
229 prepared to receive other events in the meanwhile. Also, notice that you have to explicitly set the helper ID to the action: this is done for added safety, to prevent the caller from accidentally invoking
230 a helper, and also because KAuth actions may be used without a helper attached (the default). In this case, action.execute() will return ActionSuccess if the authentication went well. This is quite useful
231 if you want your user to authenticate before doing something, which however needs no privileged permissions implementation-wise.
232
233
234 @section kauth_async Asynchronous calls, data reporting, and action termination
235
236 For a more advanced example, we look at the action "org.kde.auth.example.longaction" in the example helper. This is an action that takes a long time to execute, so we need some features:
237 - The helper needs to regularly send data to the application, to inform about the execution status.
238 - The application needs to be able to stop the action execution if the user stops it or close the application.
239 The example code follows:
240 @code
241 ActionReply MyHelper::longaction(QVariantMap args)
242 {
243 for (int i = 1; i <= 100; i++) {
244 if (HelperSupport::isStopped())
245 break;
246 HelperSupport::progressStep(i);
247 usleep(250000);
248 }
249 return ActionReply::SuccessReply;
250 }
251 @endcode
252
253 In this example, the action is only waiting a "long" time using a loop, but we can see some interesting line. The progress status is sent to the application using the HelperSupport::progressStep() method.
254 When this method is called, the ActionWatcher associated with this action will emit the progressStep() signal, reporting back the data to the application. There are two overloads of these methods and
255 corresponding signals. The one used here takes an integer. Its meaning is application dependent, so you can use it as a sort of percentage. The other overload takes a QVariantMap object that is directly
256 passed to the app. In this way, you can report to the application all the custom data you want.
257
258 In this example code, the loop exits when the HelperSupport::isStopped() returns true. This happens when the application calls the stop() method on the correponding action object. The stop() method, this
259 way, asks the helper to stop the action execution. It's up to the helper to obbey to this request, and if it does so, it should return from the slot, _not_ exit.
260
261 The code that calls the action in the application connects a slot to the actionPerformed() signal and then call executeAsync(). The progressStep() signal is directly connected to a QProgressBar, and
262 the Stop button in the UI is connected to a slot that calls the Action::stop() method.
263
264 @code
265 void MainWindow::on_longAction_triggered()
266 {
267 Action longAction = "org.kde.auth.example.longaction";
268 connect(longAction.watcher(), SIGNAL(progressStep(int)),
269 progressBar, SLOT(setValue(int)));
270 connect(longAction.watcher(), SIGNAL(actionPerformed(ActionReply)),
271 this, SLOT(longActionPerformed(ActionReply)));
272
273 longAction.setExecutesAsync(true);
274 if (longAction.execute() != Action::Authorized)
275 this->statusBar()->showMessage("Could not execute the long action");
276
277 //...
278 }
279
280 void MainWindow::stopLongAction()
281 {
282 Action("org.kde.auth.example.longaction").stop();
283 }
284
285 void MainWindow::longActionPerformed(ActionReply reply)
286 {
287 //...
288
289 if (reply.succeeded())
290 this->statusBar()->showMessage("Action succeeded", 10000);
291 else
292 this->statusBar()->showMessage(QString("Could not execute the long action: %1").arg(reply.errorCode()), 10000);
293 }
294 @endcode
295
296 Please pay attention that when you call an action, the helper will be busy executing that action. Therefore, you can't call two execute() methods in sequence like that:
297
298 @code
299 action1.execute();
300 action2.execute();
301 @endcode
302
303 If you do, you'll get a HelperBusy reply from the second action.
304 A solution would be to launch the second action from the slot connected to the first's actionPerformed signal, but this would be very ugly. Read further to know how to solve this problem.
305
306 @section kauth_other Other features
307
308 To allow to easily execute several actions in sequence, you can execute them in a group. This means using the Action::executeActions() static method, which takes a list of actions and asks the helper
309 to execute them with a single request. The helper will execute the actions in the specified order. All the signals will be emitted from the watchers associated with each action.
310
311 Sometimes the application needs to know when a particular action has started to execute. For this purpose, you can connect to the actionStarted() signal. It is emitted immediately before the helper's
312 slot is called. This isn't very useful if you call execute(), but if you use executeActions() it lets you know when individual actions in the group are started.
313
314 It doesn't happen very frequently that you code something that doesn't require some debugging, and you'll need some tool, even a basic one, to debug your helper code as well. For this reason, the
315 KDE Authorization library provides a message handler for the Qt debugging system. This means that every call to qDebug() & co. will be reported to the application, and printed using the same qt debugging
316 system, with the same debug level.
317 If, in the helper code, you write something like:
318 @code
319 qDebug() << "I'm in the helper";
320 @endcode
321 You'll see something like this in the <i>application</i>'s output:
322
323 @verbatim
324 Debug message from the helper: I'm in the helper
325 @endverbatim
326
327 Remember that the debug level is preserved, so if you use qFatal() you won't only abort the helper (which isn't suggested anyway), but also the application.
328
329
330
331 */
332namespace KAuth
333{
334
335/**
336* @brief Class that encapsulates a reply coming from the helper after executing an action
337*
338* An instance of ActionReply is returned every time you execute an action with the Action
339* class. You get the reply directly from the Action::execute() method or indirectly as
340* a parameter of the ActionWatcher::actionPerformed() signal.
341*
342* ActionReply objects can contain both data from a successful action or an error indicator.
343* In case of success, the errorCode() is ActionReply::NoError (zero) and the type() is
344* ActionReply::Success. The data() method returns a QVariantMap object that may contain
345* custom data sent back by the helper.
346*
347* In case of errors coming from the library, the type() is ActionReply::KAuthError. In this
348* case, errorCode() will always be one of the predefined errors from the ActionReply::Error enum.
349* An error reply of KAuthError type always contains an empty data() object. For some kind of errors
350* you could get a human-readable description with errorDescription().
351*
352* If, instead, the helper itself has to report some errors occurred during the action execution,
353* the type() will be (and has to be) ActionReply::HelperError. In this case the data() object can
354* contain custom data from the helper, and the errorCode() and errorDescription() values are
355* application-dependent.
356*
357* In the helper, to create an action reply object you have two choices: using the constructor, or
358* the predefined replies. For example, to create a successful reply you can use the default constructor
359* but to create a helper error reply, instead of writing <i>ActionReply(ActionReply::HelperError)</i>
360* you could use the more convenient <i>ActionReply::HelperErrorReply</i> constant.
361*
362* You should not use the predefined error replies to create and return new errors. Replies with the
363* KAuthError type are intended to be returned by the library only. However, you can use them for
364* comparisons.
365*
366* To quickly check for success or failure of an action, you can use succeeded() or failed().
367*
368* @since 4.4
369*/
370class KDECORE_EXPORT ActionReply
371{
372 class Private;
373 Private * const d;
374
375public:
376 /**
377 * Enumeration of the different kinds of replies.
378 */
379 enum Type {
380 KAuthError, ///< An error reply generated by the library itself.
381 HelperError, ///< An error reply generated by the helper.
382 Success ///< The action has been completed successfully
383 };
384
385 static const ActionReply SuccessReply; ///< An empty successful reply. Same as using the default constructor
386 static const ActionReply HelperErrorReply; ///< An empty reply with type() == HelperError.
387
388 static const ActionReply NoResponderReply; ///< errorCode() == NoResponder
389 static const ActionReply NoSuchActionReply; ///< errorCode() == NoSuchAction
390 static const ActionReply InvalidActionReply; ///< errorCode() == InvalidAction
391 static const ActionReply AuthorizationDeniedReply; ///< errorCode() == AuthorizationDenied
392 static const ActionReply UserCancelledReply; ///< errorCode() == UserCancelled
393 static const ActionReply HelperBusyReply; ///< errorCode() == HelperBusy
394 static const ActionReply DBusErrorReply; ///< errorCode() == DBusError
395
396 /**
397 * The enumeration of the possible values of errorCode() when type() is ActionReply::KAuthError
398 */
399 enum Error {
400 NoError = 0, ///< No error.
401 NoResponder, ///< The helper responder object hasn't been set. This shouldn't happen if you use the KDE4_AUTH_HELPER macro in the helper source
402 NoSuchAction, ///< The action you tried to execute doesn't exist.
403 InvalidAction, ///< You tried to execute an invalid action object
404 AuthorizationDenied, ///< You don't have the authorization to execute the action
405 UserCancelled, ///< Action execution has been cancelled by the user
406 HelperBusy, ///< The helper is busy executing another action (or group of actions). Try later
407 DBusError ///< An error from D-Bus occurred
408 };
409
410 /// Default constructor. Sets type() to Success and errorCode() to zero.
411 ActionReply();
412
413 /**
414 * @brief Constructor to directly set the type.
415 *
416 * This constructor directly sets the reply type. You shouldn't need to
417 * directly call this constructor, because you can use the more convenient
418 * predefined replies constants. You also shouldn't create a reply with
419 * the KAuthError type because it's reserved for errors coming from the
420 * library.
421 *
422 * @param type The type of the new reply
423 */
424 ActionReply(Type type);
425
426 /**
427 * @brief Constructor that creates a KAuthError reply with a specified error code.
428 * Do not use outside the library.
429 *
430 * This constructor is for internal use only, since it creates a reply
431 * with KAuthError type, which is reserved for errors coming from the library.
432 *
433 * @param errorCode The error code of the new reply
434 */
435 ActionReply(int errorCode);
436
437 /// Copy constructor
438 ActionReply(const ActionReply &reply);
439
440 /// Virtual destructor
441 virtual ~ActionReply();
442
443 /**
444 * @brief Sets the custom data to send back to the application
445 *
446 * In the helper's code you can use this function to set an QVariantMap
447 * with custom data that will be sent back to the application.
448 *
449 * @param data The new QVariantMap object.
450 */
451 void setData(const QVariantMap &data);
452
453 /**
454 * @brief Returns the custom data coming from the helper.
455 *
456 * This method is used to get the object that contains the custom
457 * data coming from the helper. In the helper's code, you can set it
458 * using setData() or the convenience method addData().
459 *
460 * @return The data coming from (or that will be sent by) the helper
461 */
462 QVariantMap data() const;
463
464 /**
465 * @brief Convenience method to add some data to the reply.
466 *
467 * This method adds the pair @c key/value to the QVariantMap used to
468 * report back custom data to the application.
469 *
470 * Use this method if you don't want to create a new QVariantMap only to
471 * add a new entry.
472 *
473 * @param key The new entry's key
474 * @param value The value of the new entry
475 */
476 void addData(const QString &key, const QVariant &value);
477
478 /// Returns the reply's type
479 Type type() const;
480
481 /**
482 * @brief Sets the reply type
483 *
484 * Every time you create an action reply, you implicitly set a type.
485 * Default constructed replies or ActionReply::SuccessReply have
486 * type() == Success.
487 * ActionReply::HelperErrorReply has type() == HelperError.
488 * Predefined error replies have type() == KAuthError.
489 *
490 * This means you rarely need to change the type after the creation,
491 * but if you need to, don't set it to KAuthError, because it's reserved
492 * for errors coming from the library.
493 *
494 * @param type The new reply type
495 */
496 void setType(Type type);
497
498 /// Returns true if type() == Success
499 bool succeeded() const;
500
501 /// Returns true if type() != Success
502 bool failed() const;
503
504 /**
505 * @brief Returns the error code of an error reply
506 *
507 * The error code returned is one of the values in the ActionReply::Error
508 * enumeration if type() == KAuthError, or is totally application-dependent if
509 * type() == HelperError. It also should be zero for successful replies.
510 *
511 * @return The reply error code
512 */
513 int errorCode() const;
514
515 /**
516 * @brief Sets the error code of an error reply
517 *
518 * If you're setting the error code in the helper because
519 * you need to return an error to the application, please make sure
520 * you already have set the type to HelperError, either by calling
521 * setType() or by creating the reply in the right way.
522 *
523 * If the type is Success when you call this method, it will become KAuthError
524 *
525 * @param errorCode The new reply error code
526 */
527 void setErrorCode(int errorCode);
528
529 /**
530 * @brief Gets a human-readble description of the error, if available
531 *
532 * Currently, replies of type KAuthError rarely report an error description.
533 * This situation could change in the future.
534 *
535 * By now, you can use this method for custom errors of type HelperError.
536 *
537 * @return The error human-readable description
538 */
539 QString errorDescription() const;
540
541 /**
542 * @brief Sets a human-readble description of the error
543 *
544 * Call this method from the helper if you want to send back a description for
545 * a custom error. Note that this method doesn't affect the errorCode in any way
546 *
547 * @param error The new error description
548 */
549 void setErrorDescription(const QString &error);
550
551 /**
552 * @brief Serialize the reply into a QByteArray.
553 *
554 * This is a convenience method used internally to sent the reply to a remote peer.
555 * To recreate the reply, use deserialize()
556 *
557 * @return A QByteArray representation of this reply
558 */
559 QByteArray serialized() const;
560
561 /**
562 * @brief Deserialize a reply from a QByteArray
563 *
564 * This method returns a reply from a QByteArray obtained from
565 * the serialized() method.
566 *
567 * @param data A QByteArray obtained with serialized()
568 */
569 static ActionReply deserialize(const QByteArray &data);
570
571 /// Assignment operator
572 ActionReply &operator=(const ActionReply &reply);
573
574 /**
575 * @brief Comparison operator
576 *
577 * This operator checks if the type and the error code of two replies are the same.
578 * It <b>doesn't</b> compare the data or the error descriptions, so be careful.
579 *
580 * The suggested use it to compare a reply agains one of the predefined error replies:
581 * @code
582 * if(reply == ActionReply::HelperBusyReply) {
583 * // Do something...
584 * }
585 * @endcode
586 *
587 * Note that you can do it also by compare errorCode() with the relative enumeration value.
588 */
589 bool operator==(const ActionReply &reply) const;
590
591 /**
592 * @brief Negated comparison operator
593 *
594 * See the operator==() for an important notice.
595 */
596 bool operator!=(const ActionReply &reply) const;
597
598 /// Output streaming operator for QDataStream
599 friend QDataStream &operator<<(QDataStream &, const ActionReply &);
600
601 /// Input streaming operator for QDataStream
602 friend QDataStream &operator>>(QDataStream &, ActionReply &);
603
604};
605
606} // namespace Auth
607
608#endif
609