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