Initial GDBus code-drop from GDBus-standalone repo
[platform/upstream/glib.git] / gio / gdbusauth.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2009 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <glib/gi18n.h>
26
27 #include "gdbusauth.h"
28 #include "gdbusauthmechanismanon.h"
29 #include "gdbusauthmechanismexternal.h"
30 #include "gdbusauthmechanismsha1.h"
31
32 #include "gdbusauthobserver.h"
33
34 #include "gdbuserror.h"
35 #include "gdbusutils.h"
36 #include "gioenumtypes.h"
37 #include "gcredentials.h"
38 #include "gdbusprivate.h"
39
40 #ifdef G_OS_UNIX
41 #include <gio/gunixconnection.h>
42 #include "gunixcredentialsmessage.h"
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #endif
46
47 #define DEBUG_ENABLED 1
48
49 static void
50 debug_print (const gchar *message, ...)
51 {
52 #if DEBUG_ENABLED
53   if (G_UNLIKELY (_g_dbus_debug_authentication ()))
54     {
55       gchar *s;
56       GString *str;
57       va_list var_args;
58       guint n;
59
60       va_start (var_args, message);
61       s = g_strdup_vprintf (message, var_args);
62       va_end (var_args);
63
64       str = g_string_new (NULL);
65       for (n = 0; s[n] != '\0'; n++)
66         {
67           if (G_UNLIKELY (s[n] == '\r'))
68             g_string_append (str, "\\r");
69           else if (G_UNLIKELY (s[n] == '\n'))
70             g_string_append (str, "\\n");
71           else
72             g_string_append_c (str, s[n]);
73         }
74       g_print ("GDBus-debug:Auth: %s\n", str->str);
75       g_string_free (str, TRUE);
76       g_free (s);
77     }
78 #endif
79 }
80
81
82 /* ---------------------------------------------------------------------------------------------------- */
83 /* TODO: move to gio */
84
85 /**
86  * g_unix_connection_send_credentials:
87  * @connection: A #GUnixConnection.
88  * @credentials: A #GCredentials to send.
89  * @cancellable: A #GCancellable or %NULL.
90  * @error: Return location for error or %NULL.
91  *
92  * Passes the credentials stored in @credentials to the recieving side
93  * of the connection. The recieving end has to call
94  * g_unix_connection_receive_credentials() (or similar) to accept the
95  * credentials.
96  *
97  * The credentials which the sender specifies are checked by the
98  * kernel.  A process with effective user ID 0 is allowed to specify
99  * values that do not match its own. This means that the credentials
100  * can be used to authenticate other connections.
101  *
102  * As well as sending the credentials this also writes a single NUL
103  * byte to the stream, as this is required for credentials passing to
104  * work on some implementations.
105  *
106  * Returns: %TRUE on success, %FALSE if @error is set.
107  *
108  * Since: 2.26
109  */
110 static gboolean
111 g_unix_connection_send_credentials (GUnixConnection      *connection,
112                                     GCredentials         *credentials,
113                                     GCancellable         *cancellable,
114                                     GError              **error)
115 {
116   GSocketControlMessage *scm;
117   GSocket *socket;
118   gboolean ret;
119   GOutputVector vector;
120   guchar nul_byte[1] = {'\0'};
121
122   g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
123   g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
124   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
125
126   ret = FALSE;
127
128   vector.buffer = &nul_byte;
129   vector.size = 1;
130   scm = g_unix_credentials_message_new_with_credentials (credentials);
131   g_object_get (connection, "socket", &socket, NULL);
132   if (g_socket_send_message (socket,
133                              NULL, /* address */
134                              &vector,
135                              1,
136                              &scm,
137                              1,
138                              G_SOCKET_MSG_NONE,
139                              cancellable,
140                              error) != 1)
141     {
142       g_prefix_error (error, _("Error sending credentials: "));
143       goto out;
144     }
145
146   ret = TRUE;
147
148  out:
149   g_object_unref (socket);
150   g_object_unref (scm);
151   return ret;
152 }
153
154 /**
155  * g_unix_connection_receive_credentials:
156  * @connection: A #GUnixConnection.
157  * @cancellable: A #GCancellable or %NULL.
158  * @error: Return location for error or %NULL.
159  *
160  * Receives credentials from the sending end of the connection.  The
161  * sending end has to call g_unix_connection_send_credentials() (or
162  * similar) for this to work.
163  *
164  * As well as reading the credentials this also reads (and discards) a
165  * single byte from the stream, as this is required for credentials
166  * passing to work on some implementations.
167  *
168  * Returns: Received credentials on success (free with
169  * g_object_unref()), %NULL if @error is set.
170  *
171  * Since: 2.26
172  */
173 static GCredentials *
174 g_unix_connection_receive_credentials (GUnixConnection      *connection,
175                                        GCancellable         *cancellable,
176                                        GError              **error)
177 {
178   GCredentials *ret;
179   GSocketControlMessage **scms;
180   gint nscm;
181   GSocket *socket;
182   gint n;
183   volatile GType credentials_message_gtype;
184   gssize num_bytes_read;
185
186   g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL);
187   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
188
189   ret = NULL;
190   scms = NULL;
191
192   g_object_get (connection, "socket", &socket, NULL);
193
194 #if 1
195   /* TODO: Move this to gsocket.c... */
196   {
197     int opt_val = 1;
198     if (setsockopt (g_socket_get_fd (socket),
199                     SOL_SOCKET,
200                     SO_PASSCRED,
201                     &opt_val,
202                     sizeof opt_val) != 0)
203       {
204         g_warning ("boo, error setting SO_PASSCRED: %m");
205       }
206   }
207 #endif
208
209   /* ensure the type of GUnixCredentialsMessage has been registered with the type system */
210   credentials_message_gtype = G_TYPE_UNIX_CREDENTIALS_MESSAGE;
211   num_bytes_read = g_socket_receive_message (socket,
212                                              NULL, /* GSocketAddress **address */
213                                              NULL,
214                                              0,
215                                              &scms,
216                                              &nscm,
217                                              NULL,
218                                              cancellable,
219                                              error);
220   if (num_bytes_read != 1)
221     {
222       /* Handle situation where g_socket_receive_message() returns
223        * 0 bytes and not setting @error
224        */
225       if (num_bytes_read == 0 && error != NULL && *error == NULL)
226         {
227           g_set_error_literal (error,
228                                G_IO_ERROR,
229                                G_IO_ERROR_FAILED,
230                                _("Expecting to read a single byte for receiving credentials but read zero bytes"));
231         }
232       goto out;
233     }
234
235   if (nscm != 1)
236     {
237       g_set_error (error,
238                    G_IO_ERROR,
239                    G_IO_ERROR_FAILED,
240                    _("Expecting 1 control message, got %d"),
241                    nscm);
242       goto out;
243     }
244
245   if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0]))
246     {
247       g_set_error_literal (error,
248                            G_IO_ERROR,
249                            G_IO_ERROR_FAILED,
250                            _("Unexpected type of ancillary data"));
251       goto out;
252     }
253
254   ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0]));
255   g_object_ref (ret);
256
257  out:
258   if (scms != NULL)
259     {
260       for (n = 0; n < nscm; n++)
261         g_object_unref (scms[n]);
262       g_free (scms);
263     }
264   g_object_unref (socket);
265   return ret;
266 }
267
268 /* ---------------------------------------------------------------------------------------------------- */
269
270 typedef struct
271 {
272   const gchar *name;
273   gint priority;
274   GType gtype;
275 } Mechanism;
276
277 static void mechanism_free (Mechanism *m);
278
279 struct _GDBusAuthPrivate
280 {
281   GIOStream *stream;
282
283   /* A list of available Mechanism, sorted according to priority  */
284   GList *available_mechanisms;
285 };
286
287 enum
288 {
289   PROP_0,
290   PROP_STREAM
291 };
292
293 G_DEFINE_TYPE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT);
294
295 /* ---------------------------------------------------------------------------------------------------- */
296
297 static void
298 _g_dbus_auth_finalize (GObject *object)
299 {
300   GDBusAuth *auth = G_DBUS_AUTH (object);
301
302   if (auth->priv->stream != NULL)
303     g_object_unref (auth->priv->stream);
304   g_list_foreach (auth->priv->available_mechanisms, (GFunc) mechanism_free, NULL);
305   g_list_free (auth->priv->available_mechanisms);
306
307   if (G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize != NULL)
308     G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize (object);
309 }
310
311 static void
312 _g_dbus_auth_get_property (GObject    *object,
313                            guint       prop_id,
314                            GValue     *value,
315                            GParamSpec *pspec)
316 {
317   GDBusAuth *auth = G_DBUS_AUTH (object);
318
319   switch (prop_id)
320     {
321     case PROP_STREAM:
322       g_value_set_object (value, auth->priv->stream);
323       break;
324
325     default:
326       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
327       break;
328     }
329 }
330
331 static void
332 _g_dbus_auth_set_property (GObject      *object,
333                            guint         prop_id,
334                            const GValue *value,
335                            GParamSpec   *pspec)
336 {
337   GDBusAuth *auth = G_DBUS_AUTH (object);
338
339   switch (prop_id)
340     {
341     case PROP_STREAM:
342       auth->priv->stream = g_value_dup_object (value);
343       break;
344
345     default:
346       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
347       break;
348     }
349 }
350
351 static void
352 _g_dbus_auth_class_init (GDBusAuthClass *klass)
353 {
354   GObjectClass *gobject_class;
355
356   g_type_class_add_private (klass, sizeof (GDBusAuthPrivate));
357
358   gobject_class = G_OBJECT_CLASS (klass);
359   gobject_class->get_property = _g_dbus_auth_get_property;
360   gobject_class->set_property = _g_dbus_auth_set_property;
361   gobject_class->finalize     = _g_dbus_auth_finalize;
362
363   g_object_class_install_property (gobject_class,
364                                    PROP_STREAM,
365                                    g_param_spec_object ("stream",
366                                                         _("IO Stream"),
367                                                         _("The underlying GIOStream used for I/O"),
368                                                         G_TYPE_IO_STREAM,
369                                                         G_PARAM_READABLE |
370                                                         G_PARAM_WRITABLE |
371                                                         G_PARAM_CONSTRUCT_ONLY |
372                                                         G_PARAM_STATIC_NAME |
373                                                         G_PARAM_STATIC_BLURB |
374                                                         G_PARAM_STATIC_NICK));
375 }
376
377 static void
378 mechanism_free (Mechanism *m)
379 {
380   g_free (m);
381 }
382
383 static void
384 add_mechanism (GDBusAuth *auth,
385                GType      mechanism_type)
386 {
387   Mechanism *m;
388
389   m = g_new0 (Mechanism, 1);
390   m->name = _g_dbus_auth_mechanism_get_name (mechanism_type);
391   m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type);
392   m->gtype = mechanism_type;
393
394   auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m);
395 }
396
397 static gint
398 mech_compare_func (Mechanism *a, Mechanism *b)
399 {
400   gint ret;
401   /* ensure deterministic order */
402   ret = b->priority - a->priority;
403   if (ret == 0)
404     ret = g_strcmp0 (b->name, a->name);
405   return ret;
406 }
407
408 static void
409 _g_dbus_auth_init (GDBusAuth *auth)
410 {
411   auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, G_TYPE_DBUS_AUTH, GDBusAuthPrivate);
412
413   /* TODO: trawl extension points */
414   add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_ANON);
415   add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_SHA1);
416   add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL);
417
418   auth->priv->available_mechanisms = g_list_sort (auth->priv->available_mechanisms,
419                                                   (GCompareFunc) mech_compare_func);
420 }
421
422 static GType
423 find_mech_by_name (GDBusAuth *auth,
424                    const gchar *name)
425 {
426   GType ret;
427   GList *l;
428
429   ret = (GType) 0;
430
431   for (l = auth->priv->available_mechanisms; l != NULL; l = l->next)
432     {
433       Mechanism *m = l->data;
434       if (g_strcmp0 (name, m->name) == 0)
435         {
436           ret = m->gtype;
437           goto out;
438         }
439     }
440
441  out:
442   return ret;
443 }
444
445 GDBusAuth  *
446 _g_dbus_auth_new (GIOStream *stream)
447 {
448   return g_object_new (G_TYPE_DBUS_AUTH,
449                        "stream", stream,
450                        NULL);
451 }
452
453 /* ---------------------------------------------------------------------------------------------------- */
454 /* like g_data_input_stream_read_line() but sets error if there's no content to read */
455 static gchar *
456 _my_g_data_input_stream_read_line (GDataInputStream  *dis,
457                                    gsize             *out_line_length,
458                                    GCancellable      *cancellable,
459                                    GError           **error)
460 {
461   gchar *ret;
462
463   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
464
465   ret = g_data_input_stream_read_line (dis,
466                                        out_line_length,
467                                        cancellable,
468                                        error);
469   if (ret == NULL && error != NULL && *error == NULL)
470     {
471       g_set_error_literal (error,
472                            G_IO_ERROR,
473                            G_IO_ERROR_FAILED,
474                            _("Unexpected lack of content trying to read a line"));
475     }
476
477   return ret;
478 }
479
480 /* This function is to avoid situations like this
481  *
482  * BEGIN\r\nl\0\0\1...
483  *
484  * e.g. where we read into the first D-Bus message while waiting for
485  * the final line from the client (TODO: file bug against gio for
486  * this)
487  */
488 static gchar *
489 _my_g_input_stream_read_line_safe (GInputStream  *i,
490                                    gsize         *out_line_length,
491                                    GCancellable  *cancellable,
492                                    GError       **error)
493 {
494   GString *str;
495   gchar c;
496   gssize num_read;
497   gboolean last_was_cr;
498
499   str = g_string_new (NULL);
500
501   last_was_cr = FALSE;
502   while (TRUE)
503     {
504       num_read = g_input_stream_read (i,
505                                       &c,
506                                       1,
507                                       cancellable,
508                                       error);
509       if (num_read == -1)
510         goto fail;
511       if (num_read == 0)
512         {
513           if (error != NULL && *error == NULL)
514             {
515               g_set_error_literal (error,
516                                    G_IO_ERROR,
517                                    G_IO_ERROR_FAILED,
518                                    _("Unexpected lack of content trying to (safely) read a line"));
519             }
520           goto fail;
521         }
522
523       g_string_append_c (str, (gint) c);
524       if (last_was_cr)
525         {
526           if (c == 0x0a)
527             {
528               g_assert (str->len >= 2);
529               g_string_set_size (str, str->len - 2);
530               goto out;
531             }
532         }
533       last_was_cr = (c == 0x0d);
534     }
535
536  out:
537   if (out_line_length != NULL)
538     *out_line_length = str->len;
539   return g_string_free (str, FALSE);
540
541  fail:
542   g_assert (error == NULL || *error != NULL);
543   g_string_free (str, TRUE);
544   return NULL;
545 }
546
547 /* ---------------------------------------------------------------------------------------------------- */
548
549 static void
550 append_nibble (GString *s, gint val)
551 {
552   g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
553 }
554
555 static gchar *
556 hexdecode (const gchar  *str,
557            gsize        *out_len,
558            GError      **error)
559 {
560   gchar *ret;
561   GString *s;
562   guint n;
563
564   ret = NULL;
565   s = g_string_new (NULL);
566
567   for (n = 0; str[n] != '\0'; n += 2)
568     {
569       gint upper_nibble;
570       gint lower_nibble;
571       guint value;
572
573       upper_nibble = g_ascii_xdigit_value (str[n]);
574       lower_nibble = g_ascii_xdigit_value (str[n + 1]);
575       if (upper_nibble == -1 || lower_nibble == -1)
576         {
577           g_set_error (error,
578                        G_IO_ERROR,
579                        G_IO_ERROR_FAILED,
580                        "Error hexdecoding string `%s' around position %d",
581                        str, n);
582           goto out;
583         }
584       value = (upper_nibble<<4) | lower_nibble;
585       g_string_append_c (s, value);
586     }
587
588   ret = g_string_free (s, FALSE);
589   s = NULL;
590
591  out:
592   if (s != NULL)
593     g_string_free (s, TRUE);
594   return ret;
595 }
596
597 /* TODO: take len */
598 static gchar *
599 hexencode (const gchar *str)
600 {
601   guint n;
602   GString *s;
603
604   s = g_string_new (NULL);
605   for (n = 0; str[n] != '\0'; n++)
606     {
607       gint val;
608       gint upper_nibble;
609       gint lower_nibble;
610
611       val = ((const guchar *) str)[n];
612       upper_nibble = val >> 4;
613       lower_nibble = val & 0x0f;
614
615       append_nibble (s, upper_nibble);
616       append_nibble (s, lower_nibble);
617     }
618
619   return g_string_free (s, FALSE);
620 }
621
622 /* ---------------------------------------------------------------------------------------------------- */
623
624 static GDBusAuthMechanism *
625 client_choose_mech_and_send_initial_response (GDBusAuth           *auth,
626                                               GCredentials        *credentials_that_were_sent,
627                                               const gchar* const  *supported_auth_mechs,
628                                               GPtrArray           *attempted_auth_mechs,
629                                               GDataOutputStream   *dos,
630                                               GCancellable        *cancellable,
631                                               GError             **error)
632 {
633   GDBusAuthMechanism *mech;
634   GType auth_mech_to_use_gtype;
635   guint n;
636   guint m;
637   gchar *initial_response;
638   gsize initial_response_len;
639   gchar *encoded;
640   gchar *s;
641
642  again:
643   mech = NULL;
644
645   debug_print ("CLIENT: Trying to choose mechanism");
646
647   /* find an authentication mechanism to try, if any */
648   auth_mech_to_use_gtype = (GType) 0;
649   for (n = 0; supported_auth_mechs[n] != NULL; n++)
650     {
651       gboolean attempted_already;
652       attempted_already = FALSE;
653       for (m = 0; m < attempted_auth_mechs->len; m++)
654         {
655           if (g_strcmp0 (supported_auth_mechs[n], attempted_auth_mechs->pdata[m]) == 0)
656             {
657               attempted_already = TRUE;
658               break;
659             }
660         }
661       if (!attempted_already)
662         {
663           auth_mech_to_use_gtype = find_mech_by_name (auth, supported_auth_mechs[n]);
664           if (auth_mech_to_use_gtype != (GType) 0)
665             break;
666         }
667     }
668
669   if (auth_mech_to_use_gtype == (GType) 0)
670     {
671       guint n;
672       gchar *available;
673       GString *tried_str;
674
675       debug_print ("CLIENT: Exhausted all available mechanisms");
676
677       available = g_strjoinv (", ", (gchar **) supported_auth_mechs);
678
679       tried_str = g_string_new (NULL);
680       for (n = 0; n < attempted_auth_mechs->len; n++)
681         {
682           if (n > 0)
683             g_string_append (tried_str, ", ");
684           g_string_append (tried_str, attempted_auth_mechs->pdata[n]);
685         }
686       g_set_error (error,
687                    G_IO_ERROR,
688                    G_IO_ERROR_FAILED,
689                    _("Exhausted all available authentication mechanisms (tried: %s) (available: %s)"),
690                    tried_str->str,
691                    available);
692       g_string_free (tried_str, TRUE);
693       g_free (available);
694       goto out;
695     }
696
697   /* OK, decided on a mechanism - let's do this thing */
698   mech = g_object_new (auth_mech_to_use_gtype,
699                        "stream", auth->priv->stream,
700                        "credentials", credentials_that_were_sent,
701                        NULL);
702   debug_print ("CLIENT: Trying mechanism `%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
703   g_ptr_array_add (attempted_auth_mechs, (gpointer) _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
704
705   /* the auth mechanism may not be supported
706    * (for example, EXTERNAL only works if credentials were exchanged)
707    */
708   if (!_g_dbus_auth_mechanism_is_supported (mech))
709     {
710       debug_print ("CLIENT: Mechanism `%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
711       g_object_unref (mech);
712       mech = NULL;
713       goto again;
714     }
715
716   initial_response_len = -1;
717   initial_response = _g_dbus_auth_mechanism_client_initiate (mech,
718                                                              &initial_response_len);
719 #if 0
720   g_printerr ("using auth mechanism with name `%s' of type `%s' with initial response `%s'\n",
721               _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
722               g_type_name (G_TYPE_FROM_INSTANCE (mech)),
723               initial_response);
724 #endif
725   if (initial_response != NULL)
726     {
727       //g_printerr ("initial_response = `%s'\n", initial_response);
728       encoded = hexencode (initial_response);
729       s = g_strdup_printf ("AUTH %s %s\r\n",
730                            _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
731                            encoded);
732       g_free (initial_response);
733       g_free (encoded);
734     }
735   else
736     {
737       s = g_strdup_printf ("AUTH %s\r\n", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
738     }
739   debug_print ("CLIENT: writing `%s'", s);
740   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
741     {
742       g_object_unref (mech);
743       mech = NULL;
744       g_free (s);
745       goto out;
746     }
747   g_free (s);
748
749  out:
750   return mech;
751 }
752
753
754 /* ---------------------------------------------------------------------------------------------------- */
755
756 typedef enum
757 {
758   CLIENT_STATE_WAITING_FOR_DATA,
759   CLIENT_STATE_WAITING_FOR_OK,
760   CLIENT_STATE_WAITING_FOR_REJECT,
761   CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD
762 } ClientState;
763
764 gchar *
765 _g_dbus_auth_run_client (GDBusAuth     *auth,
766                          GDBusCapabilityFlags offered_capabilities,
767                          GDBusCapabilityFlags *out_negotiated_capabilities,
768                          GCancellable  *cancellable,
769                          GError       **error)
770 {
771   gchar *s;
772   GDataInputStream *dis;
773   GDataOutputStream *dos;
774   GCredentials *credentials;
775   gchar *ret_guid;
776   gchar *line;
777   gsize line_length;
778   gchar **supported_auth_mechs;
779   GPtrArray *attempted_auth_mechs;
780   GDBusAuthMechanism *mech;
781   ClientState state;
782   GDBusCapabilityFlags negotiated_capabilities;
783
784   debug_print ("CLIENT: initiating");
785
786   ret_guid = NULL;
787   supported_auth_mechs = NULL;
788   attempted_auth_mechs = g_ptr_array_new ();
789   mech = NULL;
790   negotiated_capabilities = 0;
791   credentials = NULL;
792
793   dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
794   dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
795
796   g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
797
798 #ifdef G_OS_UNIX
799   if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ())
800     {
801       credentials = g_credentials_new_for_process ();
802       if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream),
803                                                credentials,
804                                                cancellable,
805                                                error))
806         goto out;
807     }
808   else
809     {
810       if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error))
811         goto out;
812     }
813 #else
814   if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error))
815     goto out;
816 #endif
817
818   if (credentials != NULL)
819     {
820       if (G_UNLIKELY (_g_dbus_debug_authentication ()))
821         {
822           s = g_credentials_to_string (credentials);
823           debug_print ("CLIENT: sent credentials `%s'", s);
824           g_free (s);
825         }
826     }
827   else
828     {
829       debug_print ("CLIENT: didn't send any credentials");
830     }
831
832   /* TODO: to reduce rountrips, try to pick an auth mechanism to start with */
833
834   /* Get list of supported authentication mechanisms */
835   s = "AUTH\r\n";
836   debug_print ("CLIENT: writing `%s'", s);
837   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
838     goto out;
839   state = CLIENT_STATE_WAITING_FOR_REJECT;
840
841   while (TRUE)
842     {
843       switch (state)
844         {
845         case CLIENT_STATE_WAITING_FOR_REJECT:
846           debug_print ("CLIENT: WaitingForReject");
847           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
848           if (line == NULL)
849             goto out;
850           debug_print ("CLIENT: WaitingForReject, read '%s'", line);
851         foobar:
852           if (!g_str_has_prefix (line, "REJECTED "))
853             {
854               g_set_error (error,
855                            G_IO_ERROR,
856                            G_IO_ERROR_FAILED,
857                            "In WaitingForReject: Expected `REJECTED am1 am2 ... amN', got `%s'",
858                            line);
859               g_free (line);
860               goto out;
861             }
862           if (supported_auth_mechs == NULL)
863             {
864               supported_auth_mechs = g_strsplit (line + sizeof ("REJECTED ") - 1, " ", 0);
865 #if 0
866               for (n = 0; supported_auth_mechs != NULL && supported_auth_mechs[n] != NULL; n++)
867                 g_printerr ("supported_auth_mechs[%d] = `%s'\n", n, supported_auth_mechs[n]);
868 #endif
869             }
870           g_free (line);
871           mech = client_choose_mech_and_send_initial_response (auth,
872                                                                credentials,
873                                                                (const gchar* const *) supported_auth_mechs,
874                                                                attempted_auth_mechs,
875                                                                dos,
876                                                                cancellable,
877                                                                error);
878           if (mech == NULL)
879             goto out;
880           if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA)
881             state = CLIENT_STATE_WAITING_FOR_DATA;
882           else
883             state = CLIENT_STATE_WAITING_FOR_OK;
884           break;
885
886         case CLIENT_STATE_WAITING_FOR_OK:
887           debug_print ("CLIENT: WaitingForOK");
888           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
889           if (line == NULL)
890             goto out;
891           debug_print ("CLIENT: WaitingForOK, read `%s'", line);
892           if (g_str_has_prefix (line, "OK "))
893             {
894               if (!g_dbus_is_guid (line + 3))
895                 {
896                   g_set_error (error,
897                                G_IO_ERROR,
898                                G_IO_ERROR_FAILED,
899                                "Invalid OK response `%s'",
900                                line);
901                   g_free (line);
902                   goto out;
903                 }
904               ret_guid = g_strdup (line + 3);
905               g_free (line);
906
907               if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
908                 {
909                   s = "NEGOTIATE_UNIX_FD\r\n";
910                   debug_print ("CLIENT: writing `%s'", s);
911                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
912                     goto out;
913                   state = CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD;
914                 }
915               else
916                 {
917                   s = "BEGIN\r\n";
918                   debug_print ("CLIENT: writing `%s'", s);
919                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
920                     goto out;
921                   /* and we're done! */
922                   goto out;
923                 }
924             }
925           else if (g_str_has_prefix (line, "REJECTED "))
926             {
927               goto foobar;
928             }
929           else
930             {
931               /* TODO: handle other valid responses */
932               g_set_error (error,
933                            G_IO_ERROR,
934                            G_IO_ERROR_FAILED,
935                            "In WaitingForOk: unexpected response `%s'",
936                            line);
937               g_free (line);
938               goto out;
939             }
940           break;
941
942         case CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD:
943           debug_print ("CLIENT: WaitingForAgreeUnixFD");
944           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
945           if (line == NULL)
946             goto out;
947           debug_print ("CLIENT: WaitingForAgreeUnixFD, read=`%s'", line);
948           if (g_strcmp0 (line, "AGREE_UNIX_FD") == 0)
949             {
950               negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
951               s = "BEGIN\r\n";
952               debug_print ("CLIENT: writing `%s'", s);
953               if (!g_data_output_stream_put_string (dos, s, cancellable, error))
954                 goto out;
955               /* and we're done! */
956               goto out;
957             }
958           else if (g_str_has_prefix (line, "ERROR") && (line[5] == 0 || g_ascii_isspace (line[5])))
959             {
960               //g_strstrip (line + 5); g_debug ("bah, no unix_fd: `%s'", line + 5);
961               g_free (line);
962               s = "BEGIN\r\n";
963               debug_print ("CLIENT: writing `%s'", s);
964               if (!g_data_output_stream_put_string (dos, s, cancellable, error))
965                 goto out;
966               /* and we're done! */
967               goto out;
968             }
969           else
970             {
971               /* TODO: handle other valid responses */
972               g_set_error (error,
973                            G_IO_ERROR,
974                            G_IO_ERROR_FAILED,
975                            "In WaitingForAgreeUnixFd: unexpected response `%s'",
976                            line);
977               g_free (line);
978               goto out;
979             }
980           break;
981
982         case CLIENT_STATE_WAITING_FOR_DATA:
983           debug_print ("CLIENT: WaitingForData");
984           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
985           if (line == NULL)
986             goto out;
987           debug_print ("CLIENT: WaitingForData, read=`%s'", line);
988           if (g_str_has_prefix (line, "DATA "))
989             {
990               gchar *encoded;
991               gchar *decoded_data;
992               gsize decoded_data_len;
993
994               encoded = g_strdup (line + 5);
995               g_free (line);
996               g_strstrip (encoded);
997               decoded_data = hexdecode (encoded, &decoded_data_len, error);
998               g_free (encoded);
999               if (decoded_data == NULL)
1000                 {
1001                   g_prefix_error (error, "DATA response is malformed: ");
1002                   /* invalid encoding, disconnect! */
1003                   goto out;
1004                 }
1005               _g_dbus_auth_mechanism_client_data_receive (mech, decoded_data, decoded_data_len);
1006               g_free (decoded_data);
1007
1008               if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND)
1009                 {
1010                   gchar *data;
1011                   gsize data_len;
1012                   gchar *encoded_data;
1013                   data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len);
1014                   encoded_data = hexencode (data);
1015                   s = g_strdup_printf ("DATA %s\r\n", encoded_data);
1016                   g_free (encoded_data);
1017                   g_free (data);
1018                   debug_print ("CLIENT: writing `%s'", s);
1019                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1020                     {
1021                       g_free (s);
1022                       goto out;
1023                     }
1024                   g_free (s);
1025                 }
1026               state = CLIENT_STATE_WAITING_FOR_OK;
1027             }
1028           else
1029             {
1030               g_set_error (error,
1031                            G_IO_ERROR,
1032                            G_IO_ERROR_FAILED,
1033                            "In WaitingForData: unexpected response `%s'",
1034                            line);
1035               g_free (line);
1036               goto out;
1037             }
1038           break;
1039
1040         default:
1041           g_assert_not_reached ();
1042           break;
1043         }
1044
1045     }; /* main authentication client loop */
1046
1047  out:
1048   if (mech != NULL)
1049     g_object_unref (mech);
1050   g_ptr_array_unref (attempted_auth_mechs);
1051   g_strfreev (supported_auth_mechs);
1052   g_object_ref (dis);
1053   g_object_ref (dos);
1054
1055   /* ensure return value is NULL if error is set */
1056   if (error != NULL && *error != NULL)
1057     {
1058       g_free (ret_guid);
1059       ret_guid = NULL;
1060     }
1061
1062   if (ret_guid != NULL)
1063     {
1064       if (out_negotiated_capabilities != NULL)
1065         *out_negotiated_capabilities = negotiated_capabilities;
1066     }
1067
1068   if (credentials != NULL)
1069     g_object_unref (credentials);
1070
1071   debug_print ("CLIENT: Done, authenticated=%d", ret_guid != NULL);
1072
1073   return ret_guid;
1074 }
1075
1076 /* ---------------------------------------------------------------------------------------------------- */
1077
1078 static gchar *
1079 get_auth_mechanisms (GDBusAuth     *auth,
1080                      gboolean       allow_anonymous,
1081                      const gchar   *prefix,
1082                      const gchar   *suffix,
1083                      const gchar   *separator)
1084 {
1085   GList *l;
1086   GString *str;
1087   gboolean need_sep;
1088
1089   str = g_string_new (prefix);
1090   need_sep = FALSE;
1091   for (l = auth->priv->available_mechanisms; l != NULL; l = l->next)
1092     {
1093       Mechanism *m = l->data;
1094
1095       if (!allow_anonymous && g_strcmp0 (m->name, "ANONYMOUS") == 0)
1096         continue;
1097
1098       if (need_sep)
1099         g_string_append (str, separator);
1100       g_string_append (str, m->name);
1101       need_sep = TRUE;
1102     }
1103
1104   g_string_append (str, suffix);
1105   return g_string_free (str, FALSE);
1106 }
1107
1108
1109 typedef enum
1110 {
1111   SERVER_STATE_WAITING_FOR_AUTH,
1112   SERVER_STATE_WAITING_FOR_DATA,
1113   SERVER_STATE_WAITING_FOR_BEGIN
1114 } ServerState;
1115
1116 gboolean
1117 _g_dbus_auth_run_server (GDBusAuth              *auth,
1118                          GDBusAuthObserver      *observer,
1119                          const gchar            *guid,
1120                          gboolean                allow_anonymous,
1121                          GDBusCapabilityFlags    offered_capabilities,
1122                          GDBusCapabilityFlags   *out_negotiated_capabilities,
1123                          GCredentials          **out_received_credentials,
1124                          GCancellable           *cancellable,
1125                          GError                **error)
1126 {
1127   gboolean ret;
1128   ServerState state;
1129   GDataInputStream *dis;
1130   GDataOutputStream *dos;
1131   GError *local_error;
1132   guchar byte;
1133   gchar *line;
1134   gsize line_length;
1135   GDBusAuthMechanism *mech;
1136   gchar *s;
1137   GDBusCapabilityFlags negotiated_capabilities;
1138   GCredentials *credentials;
1139
1140   debug_print ("SERVER: initiating");
1141
1142   ret = FALSE;
1143   dis = NULL;
1144   dos = NULL;
1145   mech = NULL;
1146   negotiated_capabilities = 0;
1147   credentials = NULL;
1148
1149   if (!g_dbus_is_guid (guid))
1150     {
1151       g_set_error (error,
1152                    G_IO_ERROR,
1153                    G_IO_ERROR_FAILED,
1154                    "The given guid `%s' is not valid",
1155                    guid);
1156       goto out;
1157     }
1158
1159   dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
1160   dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
1161
1162   g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
1163
1164   /* first read the NUL-byte (TODO: read credentials if using a unix domain socket) */
1165 #ifdef G_OS_UNIX
1166   if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ())
1167     {
1168       local_error = NULL;
1169       credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream),
1170                                                            cancellable,
1171                                                            &local_error);
1172       if (credentials == NULL)
1173         {
1174           g_propagate_error (error, local_error);
1175           goto out;
1176         }
1177     }
1178   else
1179     {
1180       local_error = NULL;
1181       byte = g_data_input_stream_read_byte (dis, cancellable, &local_error);
1182       if (local_error != NULL)
1183         {
1184           g_propagate_error (error, local_error);
1185           goto out;
1186         }
1187     }
1188 #else
1189   local_error = NULL;
1190   byte = g_data_input_stream_read_byte (dis, cancellable, &local_error);
1191   if (local_error != NULL)
1192     {
1193       g_propagate_error (error, local_error);
1194       goto out;
1195     }
1196 #endif
1197   if (credentials != NULL)
1198     {
1199       if (G_UNLIKELY (_g_dbus_debug_authentication ()))
1200         {
1201           s = g_credentials_to_string (credentials);
1202           debug_print ("SERVER: received credentials `%s'", s);
1203           g_free (s);
1204         }
1205     }
1206   else
1207     {
1208       debug_print ("SERVER: didn't receive any credentials");
1209     }
1210
1211   state = SERVER_STATE_WAITING_FOR_AUTH;
1212   while (TRUE)
1213     {
1214       switch (state)
1215         {
1216         case SERVER_STATE_WAITING_FOR_AUTH:
1217           debug_print ("SERVER: WaitingForAuth");
1218           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
1219           debug_print ("SERVER: WaitingForAuth, read `%s'", line);
1220           if (line == NULL)
1221             goto out;
1222           if (g_strcmp0 (line, "AUTH") == 0)
1223             {
1224               s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
1225               debug_print ("SERVER: writing `%s'", s);
1226               if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1227                 {
1228                   g_free (s);
1229                   goto out;
1230                 }
1231               g_free (s);
1232               g_free (line);
1233             }
1234           else if (g_str_has_prefix (line, "AUTH "))
1235             {
1236               gchar **tokens;
1237               const gchar *encoded;
1238               const gchar *mech_name;
1239               GType auth_mech_to_use_gtype;
1240
1241               tokens = g_strsplit (line, " ", 0);
1242               g_free (line);
1243
1244               switch (g_strv_length (tokens))
1245                 {
1246                 case 2:
1247                   /* no initial response */
1248                   mech_name = tokens[1];
1249                   encoded = NULL;
1250                   break;
1251
1252                 case 3:
1253                   /* initial response */
1254                   mech_name = tokens[1];
1255                   encoded = tokens[2];
1256                   break;
1257
1258                 default:
1259                   g_set_error (error,
1260                                G_IO_ERROR,
1261                                G_IO_ERROR_FAILED,
1262                                "Unexpected line `%s' while in WaitingForAuth state",
1263                                line);
1264                   g_strfreev (tokens);
1265                   goto out;
1266                 }
1267
1268               /* TODO: record that the client has attempted to use this mechanism */
1269               //g_debug ("client is trying `%s'", mech_name);
1270
1271               auth_mech_to_use_gtype = find_mech_by_name (auth, mech_name);
1272               if ((auth_mech_to_use_gtype == (GType) 0) ||
1273                   (!allow_anonymous && g_strcmp0 (mech_name, "ANONYMOUS") == 0))
1274                 {
1275                   /* We don't support this auth mechanism */
1276                   g_strfreev (tokens);
1277                   s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
1278                   debug_print ("SERVER: writing `%s'", s);
1279                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1280                     {
1281                       g_free (s);
1282                       goto out;
1283                     }
1284                   g_free (s);
1285
1286                   /* stay in WAITING FOR AUTH */
1287                   state = SERVER_STATE_WAITING_FOR_AUTH;
1288                 }
1289               else
1290                 {
1291                   gchar *initial_response;
1292                   gsize initial_response_len;
1293
1294                   mech = g_object_new (auth_mech_to_use_gtype,
1295                                        "stream", auth->priv->stream,
1296                                        "credentials", credentials,
1297                                        NULL);
1298
1299                   initial_response = NULL;
1300                   initial_response_len = 0;
1301                   if (encoded != NULL)
1302                     {
1303                       initial_response = hexdecode (encoded, &initial_response_len, error);
1304                       if (initial_response == NULL)
1305                         {
1306                           g_prefix_error (error, "Initial response is malformed: ");
1307                           /* invalid encoding, disconnect! */
1308                           g_strfreev (tokens);
1309                           goto out;
1310                         }
1311                     }
1312
1313                   _g_dbus_auth_mechanism_server_initiate (mech,
1314                                                           initial_response,
1315                                                           initial_response_len);
1316                   g_free (initial_response);
1317                   g_strfreev (tokens);
1318
1319                 change_state:
1320                   switch (_g_dbus_auth_mechanism_server_get_state (mech))
1321                     {
1322                     case G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED:
1323                       if (observer != NULL &&
1324                           g_dbus_auth_observer_deny_authenticated_peer (observer,
1325                                                                         auth->priv->stream,
1326                                                                         credentials))
1327                         {
1328                           /* disconnect */
1329                           g_set_error_literal (error,
1330                                                G_IO_ERROR,
1331                                                G_IO_ERROR_FAILED,
1332                                                _("Cancelled via GDBusAuthObserver::deny-authenticated-peer"));
1333                           goto out;
1334                         }
1335                       else
1336                         {
1337                           s = g_strdup_printf ("OK %s\r\n", guid);
1338                           debug_print ("SERVER: writing `%s'", s);
1339                           if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1340                             {
1341                               g_free (s);
1342                               goto out;
1343                             }
1344                           g_free (s);
1345                           state = SERVER_STATE_WAITING_FOR_BEGIN;
1346                         }
1347                       break;
1348
1349                     case G_DBUS_AUTH_MECHANISM_STATE_REJECTED:
1350                       s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
1351                       debug_print ("SERVER: writing `%s'", s);
1352                       if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1353                         {
1354                           g_free (s);
1355                           goto out;
1356                         }
1357                       g_free (s);
1358                       state = SERVER_STATE_WAITING_FOR_AUTH;
1359                       break;
1360
1361                     case G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA:
1362                       state = SERVER_STATE_WAITING_FOR_DATA;
1363                       break;
1364
1365                     case G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND:
1366                       {
1367                         gchar *data;
1368                         gsize data_len;
1369                         gchar *encoded_data;
1370                         data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len);
1371                         encoded_data = hexencode (data);
1372                         s = g_strdup_printf ("DATA %s\r\n", encoded_data);
1373                         g_free (encoded_data);
1374                         g_free (data);
1375                         debug_print ("SERVER: writing `%s'", s);
1376                         if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1377                           {
1378                             g_free (s);
1379                             goto out;
1380                           }
1381                         g_free (s);
1382                       }
1383                       goto change_state;
1384                       break;
1385
1386                     default:
1387                       /* TODO */
1388                       g_assert_not_reached ();
1389                       break;
1390                     }
1391                 }
1392             }
1393           else
1394             {
1395               g_set_error (error,
1396                            G_IO_ERROR,
1397                            G_IO_ERROR_FAILED,
1398                            "Unexpected line `%s' while in WaitingForAuth state",
1399                            line);
1400               g_free (line);
1401               goto out;
1402             }
1403           break;
1404
1405         case SERVER_STATE_WAITING_FOR_DATA:
1406           debug_print ("SERVER: WaitingForData");
1407           line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
1408           debug_print ("SERVER: WaitingForData, read `%s'", line);
1409           if (line == NULL)
1410             goto out;
1411           if (g_str_has_prefix (line, "DATA "))
1412             {
1413               gchar *encoded;
1414               gchar *decoded_data;
1415               gsize decoded_data_len;
1416
1417               encoded = g_strdup (line + 5);
1418               g_free (line);
1419               g_strstrip (encoded);
1420               decoded_data = hexdecode (encoded, &decoded_data_len, error);
1421               g_free (encoded);
1422               if (decoded_data == NULL)
1423                 {
1424                   g_prefix_error (error, "DATA response is malformed: ");
1425                   /* invalid encoding, disconnect! */
1426                   goto out;
1427                 }
1428               _g_dbus_auth_mechanism_server_data_receive (mech, decoded_data, decoded_data_len);
1429               g_free (decoded_data);
1430               /* oh man, this goto-crap is so ugly.. really need to rewrite the state machine */
1431               goto change_state;
1432             }
1433           else
1434             {
1435               g_set_error (error,
1436                            G_IO_ERROR,
1437                            G_IO_ERROR_FAILED,
1438                            "Unexpected line `%s' while in WaitingForData state",
1439                            line);
1440               g_free (line);
1441             }
1442           goto out;
1443
1444         case SERVER_STATE_WAITING_FOR_BEGIN:
1445           debug_print ("SERVER: WaitingForBegin");
1446           /* Use extremely slow (but reliable) line reader - this basically
1447            * does a recvfrom() system call per character
1448            *
1449            * (the problem with using GDataInputStream's read_line is that because of
1450            * buffering it might start reading into the first D-Bus message that
1451            * appears after "BEGIN\r\n"....)
1452            */
1453           line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
1454                                                     &line_length,
1455                                                     cancellable,
1456                                                     error);
1457           debug_print ("SERVER: WaitingForBegin, read `%s'", line);
1458           if (line == NULL)
1459             goto out;
1460           if (g_strcmp0 (line, "BEGIN") == 0)
1461             {
1462               /* YAY, done! */
1463               ret = TRUE;
1464               g_free (line);
1465               goto out;
1466             }
1467           else if (g_strcmp0 (line, "NEGOTIATE_UNIX_FD") == 0)
1468             {
1469               if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
1470                 {
1471                   negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
1472                   s = "AGREE_UNIX_FD\r\n";
1473                   debug_print ("SERVER: writing `%s'", s);
1474                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1475                     goto out;
1476                 }
1477               else
1478                 {
1479                   s = "ERROR \"fd passing not offered\"\r\n";
1480                   debug_print ("SERVER: writing `%s'", s);
1481                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1482                     goto out;
1483                 }
1484             }
1485           else
1486             {
1487               g_debug ("Unexpected line `%s' while in WaitingForBegin state", line);
1488               g_free (line);
1489               s = "ERROR \"Unknown Command\"\r\n";
1490               debug_print ("SERVER: writing `%s'", s);
1491               if (!g_data_output_stream_put_string (dos, s, cancellable, error))
1492                 goto out;
1493             }
1494           break;
1495
1496         default:
1497           g_assert_not_reached ();
1498           break;
1499         }
1500     }
1501
1502
1503   g_set_error_literal (error,
1504                        G_IO_ERROR,
1505                        G_IO_ERROR_FAILED,
1506                        "Not implemented (server)");
1507
1508  out:
1509   if (mech != NULL)
1510     g_object_unref (mech);
1511   if (dis != NULL)
1512     g_object_ref (dis);
1513   if (dis != NULL)
1514     g_object_ref (dos);
1515
1516   /* ensure return value is FALSE if error is set */
1517   if (error != NULL && *error != NULL)
1518     {
1519       ret = FALSE;
1520     }
1521
1522   if (ret)
1523     {
1524       if (out_negotiated_capabilities != NULL)
1525         *out_negotiated_capabilities = negotiated_capabilities;
1526       if (out_received_credentials != NULL)
1527         *out_received_credentials = credentials != NULL ? g_object_ref (credentials) : NULL;
1528     }
1529
1530   if (credentials != NULL)
1531     g_object_unref (credentials);
1532
1533   debug_print ("SERVER: Done, authenticated=%d", ret);
1534
1535   return ret;
1536 }
1537
1538 /* ---------------------------------------------------------------------------------------------------- */