1 /*
2     SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #ifndef KCUPSCONNECTION_H
8 #define KCUPSCONNECTION_H
9 
10 #include <QThread>
11 #include <QTimer>
12 #include <QVariantHash>
13 #include <QStringList>
14 #include <QWidget>
15 #include <QMetaMethod>
16 #include <QMutex>
17 
18 #include <QUrl>
19 
20 #include <cups/cups.h>
21 
22 #define KCUPS_DEVICE_CLASS          QLatin1String("device-class")
23 #define KCUPS_DEVICE_ID             QLatin1String("device-id")
24 #define KCUPS_DEVICE_INFO           QLatin1String("device-info")
25 #define KCUPS_DEVICE_MAKE_AND_MODEL QLatin1String("device-make-and-model")
26 #define KCUPS_DEVICE_LOCATION       QLatin1String("device-location")
27 #define KCUPS_DEVICE_URI            QLatin1String("device-uri")
28 
29 #define KCUPS_PRINTER_NAME                   QLatin1String("printer-name")
30 #define KCUPS_PRINTER_LOCATION               QLatin1String("printer-location")
31 #define KCUPS_PRINTER_INFO                   QLatin1String("printer-info")
32 #define KCUPS_PRINTER_URI                    QLatin1String("printer-uri")
33 #define KCUPS_PRINTER_MAKE_AND_MODEL         QLatin1String("printer-make-and-model")
34 #define KCUPS_PRINTER_STATE                  QLatin1String("printer-state")
35 #define KCUPS_PRINTER_STATE_MESSAGE          QLatin1String("printer-state-message")
36 #define KCUPS_PRINTER_IS_SHARED              QLatin1String("printer-is-shared")
37 #define KCUPS_PRINTER_IS_ACCEPTING_JOBS      QLatin1String("printer-is-accepting-jobs")
38 #define KCUPS_PRINTER_TYPE                   QLatin1String("printer-type")
39 #define KCUPS_PRINTER_TYPE_MASK              QLatin1String("printer-type-mask")
40 #define KCUPS_PRINTER_COMMANDS               QLatin1String("printer-commands")
41 #define KCUPS_PRINTER_URI_SUPPORTED          QLatin1String("printer-uri-supported")
42 #define KCUPS_PRINTER_ERROR_POLICY           QLatin1String("printer-error-policy")
43 #define KCUPS_PRINTER_ERROR_POLICY_SUPPORTED QLatin1String("printer-error-policy-supported")
44 #define KCUPS_PRINTER_OP_POLICY              QLatin1String("printer-op-policy")
45 #define KCUPS_PRINTER_OP_POLICY_SUPPORTED    QLatin1String("printer-op-policy-supported")
46 
47 #define KCUPS_MEMBER_URIS  QLatin1String("member-uris")
48 #define KCUPS_MEMBER_NAMES QLatin1String("member-names")
49 
50 #define KCUPS_MARKER_CHANGE_TIME QLatin1String("marker-change-time")
51 #define KCUPS_MARKER_COLORS      QLatin1String("marker-colors")
52 #define KCUPS_MARKER_LEVELS      QLatin1String("marker-levels")
53 #define KCUPS_MARKER_HIGH_LEVELS "marker-high-levels"
54 #define KCUPS_MARKER_LOW_LEVELS  "marker-low-levels"
55 #define KCUPS_MARKER_NAMES       QLatin1String("marker-names")
56 #define KCUPS_MARKER_TYPES       QLatin1String("marker-types")
57 #define KCUPS_MARKER_MESSAGE     "marker-message"
58 
59 #define KCUPS_JOB_ID                     QLatin1String("job-id")
60 #define KCUPS_JOB_NAME                   QLatin1String("job-name")
61 #define KCUPS_JOB_K_OCTETS               QLatin1String("job-k-octets")
62 #define KCUPS_JOB_K_OCTETS_PROCESSED     QLatin1String("job-k-octets-processed")
63 #define KCUPS_JOB_PRINTER_URI            QLatin1String("job-printer-uri")
64 #define KCUPS_JOB_PRINTER_STATE_MESSAGE  QLatin1String("job-printer-state-message")
65 #define KCUPS_JOB_ORIGINATING_USER_NAME  QLatin1String("job-originating-user-name")
66 #define KCUPS_JOB_ORIGINATING_HOST_NAME  QLatin1String("job-originating-host-name")
67 #define KCUPS_JOB_MEDIA_PROGRESS         QLatin1String("job-media-progress")
68 #define KCUPS_JOB_MEDIA_SHEETS           QLatin1String("job-media-sheets")
69 #define KCUPS_JOB_MEDIA_SHEETS_COMPLETED QLatin1String("job-media-sheets-completed")
70 #define KCUPS_JOB_PRESERVED              QLatin1String("job-preserved")
71 #define KCUPS_JOB_STATE                  QLatin1String("job-state")
72 #define KCUPS_JOB_STATE_REASONS          QLatin1String("job-state-reasons")
73 #define KCUPS_JOB_HOLD_UNTIL             QLatin1String("job-hold-until")
74 #define KCUPS_JOB_SHEETS_DEFAULT         QLatin1String("job-sheets-default")
75 #define KCUPS_JOB_SHEETS_SUPPORTED       QLatin1String("job-sheets-supported")
76 #define KCUPS_JOB_SHEETS_DEFAULT         QLatin1String("job-sheets-default")
77 #define KCUPS_JOB_SHEETS_SUPPORTED       QLatin1String("job-sheets-supported")
78 
79 #define KCUPS_MY_JOBS QLatin1String("my-jobs")
80 #define KCUPS_WHICH_JOBS QLatin1String("which-jobs")
81 
82 #define KCUPS_TIME_AT_COMPLETED  QLatin1String("time-at-completed")
83 #define KCUPS_TIME_AT_CREATION   QLatin1String("time-at-creation")
84 #define KCUPS_TIME_AT_PROCESSING QLatin1String("time-at-processing")
85 
86 #define KCUPS_REQUESTED_ATTRIBUTES QLatin1String("requested-attributes")
87 
88 #define KCUPS_REQUESTING_USER_NAME         QLatin1String("requesting-user-name")
89 #define KCUPS_REQUESTING_USER_NAME_ALLOWED QLatin1String("requesting-user-name-allowed")
90 #define KCUPS_REQUESTING_USER_NAME_DENIED  QLatin1String("requesting-user-name-denied")
91 
92 #define KCUPS_PPD_MAKE_AND_MODEL QLatin1String("ppd-make-and-model")
93 
94 #define KCUPS_NOTIFY_EVENTS          QLatin1String("notify-events")
95 #define KCUPS_NOTIFY_PULL_METHOD     QLatin1String("notify-pull-method")
96 #define KCUPS_NOTIFY_RECIPIENT_URI   QLatin1String("notify-recipient-uri")
97 #define KCUPS_NOTIFY_LEASE_DURATION  QLatin1String("notify-lease-duration")
98 #define KCUPS_NOTIFY_SUBSCRIPTION_ID QLatin1String("notify-subscription-id")
99 
100 #define KCUPS_AUTH_INFO          QLatin1String("auth-info")
101 #define KCUPS_AUTH_INFO_REQUIRED QLatin1String("auth-info-required")
102 
103 typedef QList<QVariantHash> ReturnArguments;
104 
105 class KIppRequest;
106 class KCupsPasswordDialog;
107 class Q_DECL_EXPORT KCupsConnection : public QThread
108 {
109     Q_OBJECT
110 public:
111     /**
112      * This is the main Cups class @author Daniel Nicoletti <dantti12@gmail.com>
113      *
114      * By calling KCupsConnection::global() you have access to it.
115      * Due to cups architecture, this class has to live on a
116      * separate thread so we avoid blocking the user interface when
117      * the cups call blocks.
118      *
119      * It is IMPORTANT that we do not create several thread
120      * for each cups request, doing so is a valid but breaks our
121      * authentication. We could tho store the user information an
122      * set the user/password every time it was needed. But I am not
123      * sure this is safe.
124      *
125      * Extending this means either adding methods to the KCupsRequest
126      * class which will move to this thread and then run.
127      */
128     static KCupsConnection* global();
129 
130     /**
131      * @brief KCupsConnection
132      * @param parent
133      *
134      * This is the default constructor that connects to the default server
135      * If you don't have any special reason for creating a connection
136      * on your own consider calling global()
137      */
138     explicit KCupsConnection(QObject *parent = nullptr);
139     explicit KCupsConnection(const QUrl &server, QObject *parent = nullptr);
140     ~KCupsConnection() override;
141 
142     void setPasswordMainWindow(WId mainwindow);
143 
144 Q_SIGNALS:
145     /**
146      * emitted when "server-started" is registered
147      */
148     void serverStarted(const QString &text);
149 
150     /**
151      * emitted when "server-stopped" is registered
152      */
153     void serverStopped(const QString &text);
154 
155     /**
156      * emitted when "server-restarted" is registered
157      */
158     void serverRestarted(const QString &text);
159 
160     /**
161      * emitted when "server-audit" is registered
162      */
163     void serverAudit(const QString &text);
164 
165 
166     /**
167      * emitted when "printer-added" is registered
168      */
169     void printerAdded(const QString &text,
170                       const QString &printerUri,
171                       const QString &printerName,
172                       uint printerState,
173                       const QString &printerStateReasons,
174                       bool printerIsAcceptingJobs);
175 
176     /**
177      * emitted when "printer-modified" is registered
178      */
179     void printerModified(const QString &text,
180                          const QString &printerUri,
181                          const QString &printerName,
182                          uint printerState,
183                          const QString &printerStateReasons,
184                          bool printerIsAcceptingJobs);
185 
186     /**
187      * emitted when "printer-deleted" is registered
188      */
189     void printerDeleted(const QString &text,
190                         const QString &printerUri,
191                         const QString &printerName,
192                         uint printerState,
193                         const QString &printerStateReasons,
194                         bool printerIsAcceptingJobs);
195 
196     /**
197      * emitted when "printer-state-changed" is registered
198      */
199     void printerStateChanged(const QString &text,
200                              const QString &printerUri,
201                              const QString &printerName,
202                              uint printerState,
203                              const QString &printerStateReasons,
204                              bool printerIsAcceptingJobs);
205 
206     /**
207      * emitted when "printer-stopped" is registered
208      */
209     void printerStopped(const QString &text,
210                         const QString &printerUri,
211                         const QString &printerName,
212                         uint printerState,
213                         const QString &printerStateReasons,
214                         bool printerIsAcceptingJobs);
215 
216     /**
217      * emitted when "printer-restarted" is registered
218      */
219     void printerRestarted(const QString &text,
220                           const QString &printerUri,
221                           const QString &printerName,
222                           uint printerState,
223                           const QString &printerStateReasons,
224                           bool printerIsAcceptingJobs);
225 
226     /**
227      * emitted when "printer-shutdown" is registered
228      */
229     void printerShutdown(const QString &text,
230                          const QString &printerUri,
231                          const QString &printerName,
232                          uint printerState,
233                          const QString &printerStateReasons,
234                          bool printerIsAcceptingJobs);
235 
236     /**
237      * emitted when "printer-media-changed" is registered
238      */
239     void printerMediaChanged(const QString &text,
240                              const QString &printerUri,
241                              const QString &printerName,
242                              uint printerState,
243                              const QString &printerStateReasons,
244                              bool printerIsAcceptingJobs);
245 
246     /**
247      * emitted when "printer-finishings-changed" is registered
248      */
249     void printerFinishingsChanged(const QString &text,
250                                   const QString &printerUri,
251                                   const QString &printerName,
252                                   uint printerState,
253                                   const QString &printerStateReasons,
254                                   bool printerIsAcceptingJobs);
255 
256 
257     /**
258      * emitted when "job-state-changed" is registered
259      */
260     void jobState(const QString &text,
261                   const QString &printerUri,
262                   const QString &printerName,
263                   uint printerState,
264                   const QString &printerStateReasons,
265                   bool printerIsAcceptingJobs,
266                   uint jobId,
267                   uint jobState,
268                   const QString &jobStateReasons,
269                   const QString &jobName,
270                   uint jobImpressionsCompleted);
271 
272     /**
273      * emitted when "job-created" is registered
274      */
275     void jobCreated(const QString &text,
276                     const QString &printerUri,
277                     const QString &printerName,
278                     uint printerState,
279                     const QString &printerStateReasons,
280                     bool printerIsAcceptingJobs,
281                     uint jobId,
282                     uint jobState,
283                     const QString &jobStateReasons,
284                     const QString &jobName,
285                     uint jobImpressionsCompleted);
286 
287     /**
288      * emitted when "job-stopped" is registered
289      */
290     void jobStopped(const QString &text,
291                     const QString &printerUri,
292                     const QString &printerName,
293                     uint printerState,
294                     const QString &printerStateReasons,
295                     bool printerIsAcceptingJobs,
296                     uint jobId,
297                     uint jobState,
298                     const QString &jobStateReasons,
299                     const QString &jobName,
300                     uint jobImpressionsCompleted);
301 
302     /**
303      * emitted when "job-config-changed" is registered
304      */
305     void jobConfigChanged(const QString &text,
306                           const QString &printerUri,
307                           const QString &printerName,
308                           uint printerState,
309                           const QString &printerStateReasons,
310                           bool printerIsAcceptingJobs,
311                           uint jobId,
312                           uint jobState,
313                           const QString &jobStateReasons,
314                           const QString &jobName,
315                           uint jobImpressionsCompleted);
316 
317     /**
318      * emitted when "job-progress" is registered
319      */
320     void jobProgress(const QString &text,
321                      const QString &printerUri,
322                      const QString &printerName,
323                      uint printerState,
324                      const QString &printerStateReasons,
325                      bool printerIsAcceptingJobs,
326                      uint jobId,
327                      uint jobState,
328                      const QString &jobStateReasons,
329                      const QString &jobName,
330                      uint jobImpressionsCompleted);
331 
332     /**
333      * emitted when "job-completed" is registered
334      */
335     void jobCompleted(const QString &text,
336                       const QString &printerUri,
337                       const QString &printerName,
338                       uint printerState,
339                       const QString &printerStateReasons,
340                       bool printerIsAcceptingJobs,
341                       uint jobId,
342                       uint jobState,
343                       const QString &jobStateReasons,
344                       const QString &jobName,
345                       uint jobImpressionsCompleted);
346 
347     void rhPrinterAdded(const QString &queueName);
348     void rhPrinterRemoved(const QString &queueName);
349     void rhQueueChanged(const QString &queueName);
350     void rhJobQueuedLocal(const QString &queueName, uint jobId, const QString &jobOwner);
351     void rhJobStartedLocal(const QString &queueName, uint jobId, const QString &jobOwner);
352 
353 protected:
354     friend class KCupsRequest;
355 
356     virtual void run() override;
357     bool readyToStart();
358     bool retry(const char *resource, int operation) const;
359     ReturnArguments request(const KIppRequest &request, ipp_tag_t groupTag = IPP_TAG_ZERO) const;
360 
361 private slots:
362     void updateSubscription();
363     void renewDBusSubscription();
364     void cancelDBusSubscription();
365 
366 protected:
367     virtual void connectNotify(const QMetaMethod & signal) override;
368     virtual void disconnectNotify(const QMetaMethod & signal) override;
369     QString eventForSignal(const QMetaMethod & signal) const;
370 
371 private:
372     void init();
373 
374     int renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events = QStringList());
375 
376     void notifierConnect(const QString &signal, QObject *receiver, const char *slot);
377 
378     static ReturnArguments parseIPPVars(ipp_t *response,
379                                         ipp_tag_t group_tag);
380     static QVariant ippAttrToVariant(ipp_attribute_t *attr);
381 
382     static KCupsConnection* m_instance;
383 
384     bool m_inited = false;
385     KCupsPasswordDialog *m_passwordDialog;
386     QUrl m_serverUrl;
387 
388     QTimer *m_subscriptionTimer;
389     QTimer *m_renewTimer;
390     QStringList m_connectedEvents; //note this updated in another thread. Always guard with m_mutex
391     QStringList m_requestedDBusEvents;
392     int m_subscriptionId = -1;
393     QMutex m_mutex;
394 };
395 
396 #endif // KCUPSCONNECTION_H
397