1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright 2012  Red Hat, Inc,
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 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 General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Marek Kasik <mkasik@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include "pp-host.h"
24 
25 #include <glib/gi18n.h>
26 
27 #define BUFFER_LENGTH 1024
28 
29 typedef struct
30 {
31   gchar *hostname;
32   gint   port;
33 } PpHostPrivate;
34 
35 G_DEFINE_TYPE_WITH_PRIVATE (PpHost, pp_host, G_TYPE_OBJECT);
36 
37 enum {
38   PROP_0 = 0,
39   PROP_HOSTNAME,
40   PROP_PORT,
41 };
42 
43 enum {
44   AUTHENTICATION_REQUIRED,
45   LAST_SIGNAL
46 };
47 
48 static guint signals[LAST_SIGNAL] = { 0 };
49 
50 static void
pp_host_finalize(GObject * object)51 pp_host_finalize (GObject *object)
52 {
53   PpHost *self = PP_HOST (object);
54   PpHostPrivate *priv = pp_host_get_instance_private (self);
55 
56   g_clear_pointer (&priv->hostname, g_free);
57 
58   G_OBJECT_CLASS (pp_host_parent_class)->finalize (object);
59 }
60 
61 static void
pp_host_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * param_spec)62 pp_host_get_property (GObject    *object,
63                       guint       prop_id,
64                       GValue     *value,
65                       GParamSpec *param_spec)
66 {
67   PpHost *self = PP_HOST (object);
68   PpHostPrivate *priv = pp_host_get_instance_private (self);
69 
70   switch (prop_id)
71     {
72       case PROP_HOSTNAME:
73         g_value_set_string (value, priv->hostname);
74         break;
75       case PROP_PORT:
76         g_value_set_int (value, priv->port);
77         break;
78       default:
79         G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
80                                            prop_id,
81                                            param_spec);
82       break;
83     }
84 }
85 
86 static void
pp_host_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * param_spec)87 pp_host_set_property (GObject      *object,
88                       guint         prop_id,
89                       const GValue *value,
90                       GParamSpec   *param_spec)
91 {
92   PpHost *self = PP_HOST (object);
93   PpHostPrivate *priv = pp_host_get_instance_private (self);
94 
95   switch (prop_id)
96     {
97       case PROP_HOSTNAME:
98         g_free (priv->hostname);
99         priv->hostname = g_value_dup_string (value);
100         break;
101       case PROP_PORT:
102         priv->port = g_value_get_int (value);
103         break;
104       default:
105         G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
106                                            prop_id,
107                                            param_spec);
108         break;
109     }
110 }
111 
112 static void
pp_host_class_init(PpHostClass * klass)113 pp_host_class_init (PpHostClass *klass)
114 {
115   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
116 
117   gobject_class->set_property = pp_host_set_property;
118   gobject_class->get_property = pp_host_get_property;
119   gobject_class->finalize = pp_host_finalize;
120 
121   g_object_class_install_property (gobject_class, PROP_HOSTNAME,
122     g_param_spec_string ("hostname",
123                          "Hostname",
124                          "The hostname",
125                          NULL,
126                          G_PARAM_READWRITE));
127 
128   g_object_class_install_property (gobject_class, PROP_PORT,
129     g_param_spec_int ("port",
130                       "Port",
131                       "The port",
132                       -1, G_MAXINT32, PP_HOST_UNSET_PORT,
133                       G_PARAM_READWRITE));
134 
135   signals[AUTHENTICATION_REQUIRED] =
136     g_signal_new ("authentication-required",
137                   G_TYPE_FROM_CLASS (klass),
138                   G_SIGNAL_RUN_LAST,
139                   0,
140                   NULL, NULL, NULL,
141                   G_TYPE_NONE, 0);
142 }
143 
144 static void
pp_host_init(PpHost * self)145 pp_host_init (PpHost *self)
146 {
147   PpHostPrivate *priv = pp_host_get_instance_private (self);
148   priv->port = PP_HOST_UNSET_PORT;
149 }
150 
151 PpHost *
pp_host_new(const gchar * hostname)152 pp_host_new (const gchar *hostname)
153 {
154   return g_object_new (PP_TYPE_HOST,
155                        "hostname", hostname,
156                        NULL);
157 }
158 
159 static gchar **
line_split(gchar * line)160 line_split (gchar *line)
161 {
162   gboolean          escaped = FALSE;
163   gboolean          quoted = FALSE;
164   gboolean          in_word = FALSE;
165   gchar           **words = NULL;
166   gchar           **result = NULL;
167   g_autofree gchar *buffer = NULL;
168   gchar             ch;
169   gint              n = 0;
170   gint              i, j = 0, k = 0;
171 
172   if (line)
173     {
174       n = strlen (line);
175       words = g_new0 (gchar *, n + 1);
176       buffer = g_new0 (gchar, n + 1);
177 
178       for (i = 0; i < n; i++)
179         {
180           ch = line[i];
181 
182           if (escaped)
183             {
184               buffer[k++] = ch;
185               escaped = FALSE;
186               continue;
187             }
188 
189           if (ch == '\\')
190             {
191               in_word = TRUE;
192               escaped = TRUE;
193               continue;
194             }
195 
196           if (in_word)
197             {
198               if (quoted)
199                 {
200                   if (ch == '"')
201                     quoted = FALSE;
202                   else
203                     buffer[k++] = ch;
204                 }
205               else if (g_ascii_isspace (ch))
206                 {
207                   words[j++] = g_strdup (buffer);
208                   memset (buffer, 0, n + 1);
209                   k = 0;
210                   in_word = FALSE;
211                 }
212               else if (ch == '"')
213                 quoted = TRUE;
214               else
215                 buffer[k++] = ch;
216             }
217           else
218             {
219               if (ch == '"')
220                 {
221                   in_word = TRUE;
222                   quoted = TRUE;
223                 }
224               else if (!g_ascii_isspace (ch))
225                 {
226                   in_word = TRUE;
227                   buffer[k++] = ch;
228                 }
229             }
230         }
231     }
232 
233   if (buffer && buffer[0] != '\0')
234     words[j++] = g_strdup (buffer);
235 
236   result = g_strdupv (words);
237   g_strfreev (words);
238 
239   return result;
240 }
241 
242 static void
_pp_host_get_snmp_devices_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)243 _pp_host_get_snmp_devices_thread (GTask        *task,
244                                   gpointer      source_object,
245                                   gpointer      task_data,
246                                   GCancellable *cancellable)
247 {
248   PpHost           *self = source_object;
249   PpHostPrivate    *priv = pp_host_get_instance_private (self);
250   g_autoptr(GPtrArray) devices = NULL;
251   g_autoptr(GError) error = NULL;
252   g_auto(GStrv)     argv = NULL;
253   g_autofree gchar *stdout_string = NULL;
254   gint              exit_status;
255 
256   devices = g_ptr_array_new_with_free_func (g_object_unref);
257 
258   argv = g_new0 (gchar *, 3);
259   argv[0] = g_strdup ("/usr/lib/cups/backend/snmp");
260   argv[1] = g_strdup (priv->hostname);
261 
262   /* Use SNMP to get printer's informations */
263   g_spawn_sync (NULL,
264                 argv,
265                 NULL,
266                 G_SPAWN_STDERR_TO_DEV_NULL,
267                 NULL,
268                 NULL,
269                 &stdout_string,
270                 NULL,
271                 &exit_status,
272                 &error);
273 
274   if (exit_status == 0 && stdout_string)
275     {
276       g_auto(GStrv)     printer_informations = NULL;
277       gint              length;
278 
279       printer_informations = line_split (stdout_string);
280       length = g_strv_length (printer_informations);
281 
282       if (length >= 4)
283         {
284           g_autofree gchar *device_name = NULL;
285           gboolean is_network_device;
286           PpPrintDevice *device;
287 
288           device_name = g_strdup (printer_informations[3]);
289           g_strcanon (device_name, ALLOWED_CHARACTERS, '-');
290           is_network_device = g_strcmp0 (printer_informations[0], "network") == 0;
291 
292           device = g_object_new (PP_TYPE_PRINT_DEVICE,
293                                  "is-network-device", is_network_device,
294                                  "device-uri", printer_informations[1],
295                                  "device-make-and-model", printer_informations[2],
296                                  "device-info", printer_informations[3],
297                                  "acquisition-method", ACQUISITION_METHOD_SNMP,
298                                  "device-name", device_name,
299                                  NULL);
300 
301           if (length >= 5 && printer_informations[4][0] != '\0')
302             g_object_set (device, "device-id", printer_informations[4], NULL);
303 
304           if (length >= 6 && printer_informations[5][0] != '\0')
305             g_object_set (device, "device-location", printer_informations[5], NULL);
306 
307           g_ptr_array_add (devices, device);
308         }
309     }
310 
311   g_task_return_pointer (task, g_ptr_array_ref (devices), (GDestroyNotify) g_ptr_array_unref);
312 }
313 
314 void
pp_host_get_snmp_devices_async(PpHost * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)315 pp_host_get_snmp_devices_async (PpHost              *self,
316                                 GCancellable        *cancellable,
317                                 GAsyncReadyCallback  callback,
318                                 gpointer             user_data)
319 {
320   g_autoptr(GTask) task = NULL;
321 
322   task = g_task_new (self, cancellable, callback, user_data);
323   g_task_run_in_thread (task, _pp_host_get_snmp_devices_thread);
324 }
325 
326 GPtrArray *
pp_host_get_snmp_devices_finish(PpHost * self,GAsyncResult * res,GError ** error)327 pp_host_get_snmp_devices_finish (PpHost        *self,
328                                  GAsyncResult  *res,
329                                  GError       **error)
330 {
331   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
332   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
333   return g_task_propagate_pointer (G_TASK (res), error);
334 }
335 
336 static void
_pp_host_get_remote_cups_devices_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)337 _pp_host_get_remote_cups_devices_thread (GTask        *task,
338                                          gpointer      source_object,
339                                          gpointer      task_data,
340                                          GCancellable *cancellable)
341 {
342   cups_dest_t   *dests = NULL;
343   PpHost        *self = (PpHost *) source_object;
344   PpHostPrivate *priv = pp_host_get_instance_private (self);
345   g_autoptr(GPtrArray) devices = NULL;
346   http_t        *http;
347   gint           num_of_devices = 0;
348   gint           port;
349   gint           i;
350 
351   devices = g_ptr_array_new_with_free_func (g_object_unref);
352 
353   if (priv->port == PP_HOST_UNSET_PORT)
354     port = PP_HOST_DEFAULT_IPP_PORT;
355   else
356     port = priv->port;
357 
358   /* Connect to remote CUPS server and get its devices */
359 #ifdef HAVE_CUPS_HTTPCONNECT2
360   http = httpConnect2 (priv->hostname, port, NULL, AF_UNSPEC,
361                        HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
362 #else
363   http = httpConnect (priv->hostname, port);
364 #endif
365   if (http)
366     {
367       num_of_devices = cupsGetDests2 (http, &dests);
368       if (num_of_devices > 0)
369         {
370           for (i = 0; i < num_of_devices; i++)
371             {
372               g_autofree gchar *device_uri = NULL;
373               const char *device_location;
374               PpPrintDevice *device;
375 
376               device_uri = g_strdup_printf ("ipp://%s:%d/printers/%s",
377                                             priv->hostname,
378                                             port,
379                                             dests[i].name);
380 
381               device_location = cupsGetOption ("printer-location",
382                                                dests[i].num_options,
383                                                dests[i].options);
384 
385               device = g_object_new (PP_TYPE_PRINT_DEVICE,
386                                      "is-network-device", TRUE,
387                                      "device-uri", device_uri,
388                                      "device-name", dests[i].name,
389                                      "device-location", device_location,
390                                      "host-name", priv->hostname,
391                                      "host-port", port,
392                                      "acquisition-method", ACQUISITION_METHOD_REMOTE_CUPS_SERVER,
393                                      NULL);
394               g_ptr_array_add (devices, device);
395             }
396         }
397 
398       httpClose (http);
399     }
400 
401   g_task_return_pointer (task, g_ptr_array_ref (devices), (GDestroyNotify) g_ptr_array_unref);
402 }
403 
404 void
pp_host_get_remote_cups_devices_async(PpHost * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)405 pp_host_get_remote_cups_devices_async (PpHost              *self,
406                                        GCancellable        *cancellable,
407                                        GAsyncReadyCallback  callback,
408                                        gpointer             user_data)
409 {
410   g_autoptr(GTask) task = NULL;
411 
412   task = g_task_new (self, cancellable, callback, user_data);
413   g_task_run_in_thread (task, _pp_host_get_remote_cups_devices_thread);
414 }
415 
416 GPtrArray *
pp_host_get_remote_cups_devices_finish(PpHost * self,GAsyncResult * res,GError ** error)417 pp_host_get_remote_cups_devices_finish (PpHost        *self,
418                                         GAsyncResult  *res,
419                                         GError       **error)
420 {
421   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
422   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
423   return g_task_propagate_pointer (G_TASK (res), error);
424 }
425 
426 typedef struct
427 {
428   PpHost *host;
429   gint    port;
430 } JetDirectData;
431 
432 static void
jetdirect_data_free(JetDirectData * data)433 jetdirect_data_free (JetDirectData *data)
434 {
435   if (data != NULL)
436     {
437       g_clear_object (&data->host);
438       g_free (data);
439     }
440 }
441 
442 static void
jetdirect_connection_test_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)443 jetdirect_connection_test_cb (GObject      *source_object,
444                               GAsyncResult *res,
445                               gpointer      user_data)
446 {
447   g_autoptr(GSocketConnection) connection = NULL;
448   PpHostPrivate               *priv;
449   JetDirectData               *data;
450   g_autoptr(GPtrArray)         devices = NULL;
451   g_autoptr(GError)            error = NULL;
452   g_autoptr(GTask)             task = G_TASK (user_data);
453 
454   data = g_task_get_task_data (task);
455   priv = pp_host_get_instance_private (data->host);
456 
457   devices = g_ptr_array_new_with_free_func (g_object_unref);
458 
459   connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
460                                                        res,
461                                                        &error);
462 
463   if (connection != NULL)
464     {
465       g_autofree gchar *device_uri = NULL;
466       PpPrintDevice *device;
467 
468       g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
469 
470       device_uri = g_strdup_printf ("socket://%s:%d",
471                                     priv->hostname,
472                                     data->port);
473 
474       device = g_object_new (PP_TYPE_PRINT_DEVICE,
475                              "is-network-device", TRUE,
476                              "device-uri", device_uri,
477                              /* Translators: The found device is a JetDirect printer */
478                              "device-name", _("JetDirect Printer"),
479                              "host-name", priv->hostname,
480                              "host-port", data->port,
481                              "acquisition-method", ACQUISITION_METHOD_JETDIRECT,
482                              NULL);
483       g_ptr_array_add (devices, device);
484     }
485 
486   g_task_return_pointer (task, g_ptr_array_ref (devices), (GDestroyNotify) g_ptr_array_unref);
487 }
488 
489 /* Test whether given host has an AppSocket/HP JetDirect printer connected.
490    See http://en.wikipedia.org/wiki/JetDirect
491        http://www.cups.org/documentation.php/network.html */
492 void
pp_host_get_jetdirect_devices_async(PpHost * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)493 pp_host_get_jetdirect_devices_async (PpHost              *self,
494                                      GCancellable        *cancellable,
495                                      GAsyncReadyCallback  callback,
496                                      gpointer             user_data)
497 {
498   PpHostPrivate    *priv = pp_host_get_instance_private (self);
499   JetDirectData    *data;
500   g_autoptr(GTask)  task = NULL;
501   g_autofree gchar *address = NULL;
502 
503   data = g_new0 (JetDirectData, 1);
504   data->host = g_object_ref (self);
505 
506   if (priv->port == PP_HOST_UNSET_PORT)
507     data->port = PP_HOST_DEFAULT_JETDIRECT_PORT;
508   else
509     data->port = priv->port;
510 
511   task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
512   g_task_set_task_data (task, data, (GDestroyNotify) jetdirect_data_free);
513 
514   address = g_strdup_printf ("%s:%d", priv->hostname, data->port);
515   if (address != NULL && address[0] != '/')
516     {
517       g_autoptr(GSocketClient) client = NULL;
518 
519       client = g_socket_client_new ();
520 
521       g_socket_client_connect_to_host_async (client,
522                                              address,
523                                              data->port,
524                                              cancellable,
525                                              jetdirect_connection_test_cb,
526                                              g_steal_pointer (&task));
527     }
528   else
529     {
530       GPtrArray *devices = g_ptr_array_new_with_free_func (g_object_unref);
531       g_task_return_pointer (task, devices, (GDestroyNotify) g_ptr_array_unref);
532     }
533 }
534 
535 GPtrArray *
pp_host_get_jetdirect_devices_finish(PpHost * self,GAsyncResult * res,GError ** error)536 pp_host_get_jetdirect_devices_finish (PpHost        *self,
537                                       GAsyncResult  *res,
538                                       GError       **error)
539 {
540   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
541   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
542   return g_task_propagate_pointer (G_TASK (res), error);
543 }
544 
545 static gboolean
test_lpd_queue(GSocketClient * client,gchar * address,gint port,GCancellable * cancellable,gchar * queue_name)546 test_lpd_queue (GSocketClient *client,
547                 gchar         *address,
548                 gint           port,
549                 GCancellable  *cancellable,
550                 gchar         *queue_name)
551 {
552   g_autoptr(GSocketConnection) connection = NULL;
553   gboolean                     result = FALSE;
554   g_autoptr(GError)            error = NULL;
555 
556   connection = g_socket_client_connect_to_host (client,
557                                                 address,
558                                                 port,
559                                                 cancellable,
560                                                 &error);
561 
562   if (connection != NULL)
563     {
564       if (G_IS_TCP_CONNECTION (connection))
565         {
566           GOutputStream *output;
567           GInputStream  *input;
568           gssize         bytes_read, bytes_written;
569           gchar          buffer[BUFFER_LENGTH];
570           gint           length;
571 
572           output = g_io_stream_get_output_stream (G_IO_STREAM (connection));
573           input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
574 
575           /* This LPD command is explained in RFC 1179, section 5.2 */
576           length = g_snprintf (buffer, BUFFER_LENGTH, "\2%s\n", queue_name);
577 
578           bytes_written = g_output_stream_write (output,
579                                                  buffer,
580                                                  length,
581                                                  NULL,
582                                                  &error);
583 
584           if (bytes_written != -1)
585             {
586               bytes_read = g_input_stream_read (input,
587                                                 buffer,
588                                                 BUFFER_LENGTH,
589                                                 NULL,
590                                                 &error);
591 
592               if (bytes_read != -1)
593                 {
594                   if (bytes_read > 0 && buffer[0] == 0)
595                     {
596                       /* This LPD command is explained in RFC 1179, section 6.1 */
597                       length = g_snprintf (buffer, BUFFER_LENGTH, "\1\n");
598 
599                       bytes_written = g_output_stream_write (output,
600                                                              buffer,
601                                                              length,
602                                                              NULL,
603                                                              &error);
604 
605                       result = TRUE;
606                     }
607                 }
608             }
609         }
610 
611       g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
612     }
613 
614   return result;
615 }
616 
617 static void
_pp_host_get_lpd_devices_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)618 _pp_host_get_lpd_devices_thread (GTask        *task,
619                                  gpointer      source_object,
620                                  gpointer      task_data,
621                                  GCancellable *cancellable)
622 {
623   g_autoptr(GSocketConnection) connection = NULL;
624   PpHost                      *self = source_object;
625   PpHostPrivate               *priv = pp_host_get_instance_private (self);
626   g_autoptr(GPtrArray)         devices = NULL;
627   g_autoptr(GSocketClient)     client = NULL;
628   g_autoptr(GError)            error = NULL;
629   GList                       *candidates = NULL;
630   GList                       *iter;
631   gchar                       *found_queue = NULL;
632   gchar                       *candidate;
633   g_autofree gchar            *address = NULL;
634   gint                         port;
635   gint                         i;
636 
637   if (priv->port == PP_HOST_UNSET_PORT)
638     port = PP_HOST_DEFAULT_LPD_PORT;
639   else
640     port = priv->port;
641 
642   devices = g_ptr_array_new_with_free_func (g_object_unref);
643 
644   address = g_strdup_printf ("%s:%d", priv->hostname, port);
645   if (address == NULL || address[0] == '/')
646     {
647       g_task_return_pointer (task, g_ptr_array_ref (devices), (GDestroyNotify) g_ptr_array_unref);
648       return;
649     }
650 
651   client = g_socket_client_new ();
652 
653   connection = g_socket_client_connect_to_host (client,
654                                                 address,
655                                                 port,
656                                                 cancellable,
657                                                 &error);
658 
659   if (connection != NULL)
660     {
661       g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
662 
663       /* Most of this list is taken from system-config-printer */
664       candidates = g_list_append (candidates, g_strdup ("PASSTHRU"));
665       candidates = g_list_append (candidates, g_strdup ("AUTO"));
666       candidates = g_list_append (candidates, g_strdup ("BINPS"));
667       candidates = g_list_append (candidates, g_strdup ("RAW"));
668       candidates = g_list_append (candidates, g_strdup ("TEXT"));
669       candidates = g_list_append (candidates, g_strdup ("ps"));
670       candidates = g_list_append (candidates, g_strdup ("lp"));
671       candidates = g_list_append (candidates, g_strdup ("PORT1"));
672 
673       for (i = 0; i < 8; i++)
674         {
675           candidates = g_list_append (candidates, g_strdup_printf ("LPT%d", i));
676           candidates = g_list_append (candidates, g_strdup_printf ("LPT%d_PASSTHRU", i));
677           candidates = g_list_append (candidates, g_strdup_printf ("COM%d", i));
678           candidates = g_list_append (candidates, g_strdup_printf ("COM%d_PASSTHRU", i));
679         }
680 
681       for (i = 0; i < 50; i++)
682         candidates = g_list_append (candidates, g_strdup_printf ("pr%d", i));
683 
684       for (iter = candidates; iter != NULL; iter = iter->next)
685         {
686           candidate = (gchar *) iter->data;
687 
688           if (test_lpd_queue (client,
689                               address,
690                               port,
691                               cancellable,
692                               candidate))
693             {
694               found_queue = g_strdup (candidate);
695               break;
696             }
697         }
698 
699       if (found_queue != NULL)
700         {
701           g_autofree gchar *device_uri = NULL;
702           PpPrintDevice *device;
703 
704           device_uri = g_strdup_printf ("lpd://%s:%d/%s",
705                                         priv->hostname,
706                                         port,
707                                         found_queue);
708 
709           device = g_object_new (PP_TYPE_PRINT_DEVICE,
710                                  "is-network-device", TRUE,
711                                  "device-uri", device_uri,
712                                  /* Translators: The found device is a Line Printer Daemon printer */
713                                  "device-name", _("LPD Printer"),
714                                  "host-name", priv->hostname,
715                                  "host-port", port,
716                                  "acquisition-method", ACQUISITION_METHOD_LPD,
717                                  NULL);
718           g_ptr_array_add (devices, device);
719         }
720 
721       g_list_free_full (candidates, g_free);
722     }
723 
724   g_task_return_pointer (task, g_ptr_array_ref (devices), (GDestroyNotify) g_ptr_array_unref);
725 }
726 
727 void
pp_host_get_lpd_devices_async(PpHost * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)728 pp_host_get_lpd_devices_async (PpHost              *self,
729                                GCancellable        *cancellable,
730                                GAsyncReadyCallback  callback,
731                                gpointer             user_data)
732 {
733   g_autoptr(GTask) task = NULL;
734 
735   task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
736   g_task_run_in_thread (task, _pp_host_get_lpd_devices_thread);
737 }
738 
739 GPtrArray *
pp_host_get_lpd_devices_finish(PpHost * self,GAsyncResult * res,GError ** error)740 pp_host_get_lpd_devices_finish (PpHost        *self,
741                                 GAsyncResult  *res,
742                                 GError       **error)
743 {
744   g_return_val_if_fail (g_task_is_valid (res, self), NULL);
745   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
746   return g_task_propagate_pointer (G_TASK (res), error);
747 }
748