]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/libsystem.c
1164b4813598fb9f31cc16e7ffea2d2373579546
[sojka/lightdm.git] / tests / src / libsystem.c
1 #define _GNU_SOURCE
2 #define __USE_GNU
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/ioctl.h>
11 #include <pwd.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <grp.h>
15 #include <security/pam_appl.h>
16 #include <fcntl.h>
17 #include <dlfcn.h>
18 #include <utmp.h>
19 #include <utmpx.h>
20 #ifdef __linux__
21 #include <linux/vt.h>
22 #endif
23 #include <glib.h>
24 #include <xcb/xcb.h>
25 #include <gio/gunixsocketaddress.h>
26
27 #include "status.h"
28
29 #define LOGIN_PROMPT "login:"
30
31 static int console_fd = -1;
32
33 static GList *user_entries = NULL;
34 static GList *getpwent_link = NULL;
35
36 static GList *group_entries = NULL;
37
38 static int active_vt = 7;
39
40 static gboolean status_connected = FALSE;
41 static GKeyFile *config;
42
43 static void connect_status (void)
44 {
45     if (status_connected)
46         return;
47     status_connected = TRUE;
48
49     status_connect (NULL, NULL);
50
51     config = g_key_file_new ();
52     g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
53 }
54
55 struct pam_handle
56 {
57     char *service_name;
58     char *user;
59     char *authtok;
60     char *ruser;
61     char *tty;
62     char **envlist;
63     struct pam_conv conversation;
64 };
65
66 uid_t
67 getuid (void)
68 {
69     return 0;
70 }
71
72 /*uid_t
73 geteuid (void)
74 {
75     return 0;
76 }*/
77
78 int
79 initgroups (const char *user, gid_t group)
80 {
81     gid_t g[1];
82
83     g[0] = group;
84     setgroups (1, g);
85
86     return 0;
87 }
88
89 int
90 getgroups (int size, gid_t list[])
91 {
92     const gchar *group_list;
93     gchar **groups;
94     gint groups_length;
95
96     /* Get groups we are a member of */
97     group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
98     if (!group_list)
99         group_list = "";
100     groups = g_strsplit (group_list, ",", -1);
101     groups_length = g_strv_length (groups);
102
103     if (size != 0)
104     {
105         int i;
106
107         if (groups_length > size)
108         {
109             errno = EINVAL;
110             return -1;
111         }
112         for (i = 0; groups[i]; i++)
113             list[i] = atoi (groups[i]);
114     }
115     g_free (groups);
116
117     return groups_length;
118 }
119
120 int
121 setgroups (size_t size, const gid_t *list)
122 {
123     size_t i;
124     GString *group_list;
125
126     group_list = g_string_new ("");
127     for (i = 0; i < size; i++)
128     {
129         if (i != 0)
130             g_string_append (group_list, ",");
131         g_string_append_printf (group_list, "%d", list[i]);
132     }
133     g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
134     g_string_free (group_list, TRUE);
135
136     return 0;
137 }
138
139 int
140 setgid (gid_t gid)
141 {
142     return 0;
143 }
144
145 int
146 setegid (gid_t gid)
147 {
148     return 0;
149 }
150
151 int
152 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
153 {
154     return 0;
155 }
156
157 int
158 setuid (uid_t uid)
159 {
160     return 0;
161 }
162
163 int
164 seteuid (uid_t uid)
165 {
166     return 0;
167 }
168
169 int
170 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
171 {
172     return 0;
173 }
174
175 static gchar *
176 redirect_path (const gchar *path)
177 {
178     // Don't redirect if inside the running directory
179     if (g_str_has_prefix (path, g_getenv ("LIGHTDM_TEST_ROOT")))
180         return g_strdup (path);
181
182     if (g_str_has_prefix (path, SYSCONFDIR))
183         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", path + strlen (SYSCONFDIR), NULL);
184
185     if (g_str_has_prefix (path, LOCALSTATEDIR))
186         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "var", path + strlen (LOCALSTATEDIR), NULL);
187
188     if (g_str_has_prefix (path, DATADIR))
189         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "usr", "share", path + strlen (DATADIR), NULL);
190
191     // Don't redirect if inside the build directory
192     if (g_str_has_prefix (path, BUILDDIR))
193         return g_strdup (path);
194
195     if (g_str_has_prefix (path, "/tmp"))
196         return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + strlen ("/tmp"), NULL);
197
198     return g_strdup (path);
199 }
200
201 #ifdef __linux__
202 static int
203 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
204 {
205     int (*_open) (const char *pathname, int flags, mode_t mode);
206     gchar *new_path = NULL;
207     int fd;
208
209     _open = (int (*)(const char *pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
210
211     if (strcmp (pathname, "/dev/console") == 0)
212     {
213         if (console_fd < 0)
214         {
215             console_fd = _open ("/dev/null", flags, mode);
216             fcntl (console_fd, F_SETFD, FD_CLOEXEC);
217         }
218         return console_fd;
219     }
220
221     new_path = redirect_path (pathname);
222     fd = _open (new_path, flags, mode);
223     g_free (new_path);
224
225     return fd;
226 }
227
228 int
229 open (const char *pathname, int flags, ...)
230 {
231     int mode = 0;
232     if (flags & O_CREAT)
233     {
234         va_list ap;
235         va_start (ap, flags);
236         mode = va_arg (ap, mode_t);
237         va_end (ap);
238     }
239     return open_wrapper ("open", pathname, flags, mode);
240 }
241
242 int
243 open64 (const char *pathname, int flags, ...)
244 {
245     int mode = 0;
246     if (flags & O_CREAT)
247     {
248         va_list ap;
249         va_start (ap, flags);
250         mode = va_arg (ap, mode_t);
251         va_end (ap);
252     }
253     return open_wrapper ("open64", pathname, flags, mode);
254 }
255
256 FILE *
257 fopen (const char *path, const char *mode)
258 {
259     FILE *(*_fopen) (const char *pathname, const char *mode);
260     gchar *new_path = NULL;
261     FILE *result;
262
263     _fopen = (FILE *(*)(const char *pathname, const char *mode)) dlsym (RTLD_NEXT, "fopen");
264
265     new_path = redirect_path (path);
266     result = _fopen (new_path, mode);
267     g_free (new_path);
268
269     return result;
270 }
271
272 int
273 unlinkat (int dirfd, const char *pathname, int flags)
274 {
275     int (*_unlinkat) (int dirfd, const char *pathname, int flags);
276     gchar *new_path = NULL;
277     int result;
278
279     _unlinkat = (int (*)(int dirfd, const char *pathname, int flags)) dlsym (RTLD_NEXT, "unlinkat");
280
281     new_path = redirect_path (pathname);
282     result = _unlinkat (dirfd, new_path, flags);
283     g_free (new_path);
284
285     return result;
286 }
287
288 int
289 creat (const char *pathname, mode_t mode)
290 {
291     int (*_creat) (const char *pathname, mode_t mode);
292     gchar *new_path = NULL;
293     int result;
294
295     _creat = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat");
296
297     new_path = redirect_path (pathname);
298     result = _creat (new_path, mode);
299     g_free (new_path);
300
301     return result;
302 }
303
304 int
305 creat64 (const char *pathname, mode_t mode)
306 {
307     int (*_creat64) (const char *pathname, mode_t mode);
308     gchar *new_path = NULL;
309     int result;
310
311     _creat64 = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "creat64");
312
313     new_path = redirect_path (pathname);
314     result = _creat64 (new_path, mode);
315     g_free (new_path);
316
317     return result;
318 }
319
320 int
321 access (const char *pathname, int mode)
322 {
323     int (*_access) (const char *pathname, int mode);
324     gchar *new_path = NULL;
325     int ret;
326
327     /* Look like systemd is always running */
328     if (strcmp (pathname, "/run/systemd/seats/") == 0)
329         return 1;
330
331     _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
332
333     new_path = redirect_path (pathname);
334     ret = _access (new_path, mode);
335     g_free (new_path);
336
337     return ret;
338 }
339
340 int
341 stat (const char *path, struct stat *buf)
342 {
343     int (*_stat) (const char *path, struct stat *buf);
344     gchar *new_path = NULL;
345     int ret;
346   
347     _stat = (int (*)(const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "stat");
348
349     new_path = redirect_path (path);
350     ret = _stat (new_path, buf);
351     g_free (new_path);
352
353     return ret;
354 }
355
356 int
357 stat64 (const char *path, struct stat64 *buf)
358 {
359     int (*_stat64) (const char *path, struct stat64 *buf);
360     gchar *new_path = NULL;
361     int ret;
362
363     _stat64 = (int (*)(const char *path, struct stat64 *buf)) dlsym (RTLD_NEXT, "stat64");
364
365     new_path = redirect_path (path);
366     ret = _stat64 (new_path, buf);
367     g_free (new_path);
368
369     return ret;
370 }
371
372 int
373 __xstat (int version, const char *path, struct stat *buf)
374 {
375     int (*___xstat) (int version, const char *path, struct stat *buf);
376     gchar *new_path = NULL;
377     int ret;
378   
379     ___xstat = (int (*)(int version, const char *path, struct stat *buf)) dlsym (RTLD_NEXT, "__xstat");
380
381     new_path = redirect_path (path);
382     ret = ___xstat (version, new_path, buf);
383     g_free (new_path);
384
385     return ret;
386 }
387
388 int
389 __xstat64 (int version, const char *path, struct stat64 *buf)
390 {
391     int (*___xstat64) (int version, const char *path, struct stat64 *buf);
392     gchar *new_path = NULL;
393     int ret;
394   
395     ___xstat64 = (int (*)(int version, const char *path, struct stat64 *buf)) dlsym (RTLD_NEXT, "__xstat64");
396
397     new_path = redirect_path (path);
398     ret = ___xstat64 (version, new_path, buf);
399     g_free (new_path);
400
401     return ret;
402 }
403
404 int
405 __fxstatat(int ver, int dirfd, const char *pathname, struct stat *buf, int flags)
406 {
407     int (*___fxstatat) (int ver, int dirfd, const char *pathname, struct stat *buf, int flags);
408     gchar *new_path = NULL;
409     int ret;
410   
411     ___fxstatat = (int (*)(int ver, int dirfd, const char *pathname, struct stat *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat");
412
413     new_path = redirect_path (pathname);
414     ret = ___fxstatat (ver, dirfd, new_path, buf, flags);
415     g_free (new_path);
416
417     return ret;
418 }
419
420 int
421 __fxstatat64(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags)
422 {
423     int (*___fxstatat64) (int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags);
424     gchar *new_path = NULL;
425     int ret;
426   
427     ___fxstatat64 = (int (*)(int ver, int dirfd, const char *pathname, struct stat64 *buf, int flags)) dlsym (RTLD_NEXT, "__fxstatat64");
428
429     new_path = redirect_path (pathname);
430     ret = ___fxstatat64 (ver, dirfd, new_path, buf, flags);
431     g_free (new_path);
432
433     return ret;
434 }
435
436 DIR *
437 opendir (const char *name)
438 {
439     DIR *(*_opendir) (const char *name);
440     gchar *new_path = NULL;
441     DIR *result;
442
443     _opendir = (DIR *(*)(const char *name)) dlsym (RTLD_NEXT, "opendir");
444
445     new_path = redirect_path (name);
446     result = _opendir (new_path);
447     g_free (new_path);
448
449     return result; 
450 }
451
452 int
453 mkdir (const char *pathname, mode_t mode)
454 {
455     int (*_mkdir) (const char *pathname, mode_t mode);
456     gchar *new_path = NULL;
457     int result;
458
459     _mkdir = (int (*)(const char *pathname, mode_t mode)) dlsym (RTLD_NEXT, "mkdir");
460
461     new_path = redirect_path (pathname);
462     result = _mkdir (new_path, mode);
463     g_free (new_path);
464
465     return result;
466 }
467
468 int
469 chown (const char *pathname, uid_t owner, gid_t group)
470 {
471     /* Just fake it - we're not root */
472     return 0;
473 }
474
475 int
476 chmod (const char *path, mode_t mode)
477 {
478     int (*_chmod) (const char *path, mode_t mode);
479     gchar *new_path = NULL;
480     int result;
481
482     _chmod = (int (*)(const char *path, mode_t mode)) dlsym (RTLD_NEXT, "chmod");
483
484     new_path = redirect_path (path);
485     result = _chmod (new_path, mode);
486     g_free (new_path);
487
488     return result;
489 }
490
491 int
492 ioctl (int d, unsigned long request, ...)
493 {
494     int (*_ioctl) (int d, int request, ...);
495
496     _ioctl = (int (*)(int d, int request, ...)) dlsym (RTLD_NEXT, "ioctl");
497     if (d > 0 && d == console_fd)
498     {
499         struct vt_stat *console_state;
500         int vt;
501         va_list ap;
502
503         switch (request)
504         {
505         case VT_GETSTATE:
506             va_start (ap, request);
507             console_state = va_arg (ap, struct vt_stat *);
508             va_end (ap);
509             console_state->v_active = active_vt;
510             break;
511         case VT_ACTIVATE:
512             va_start (ap, request);
513             vt = va_arg (ap, int);
514             va_end (ap);
515             if (vt != active_vt)
516             {
517                 active_vt = vt;
518                 connect_status ();
519                 status_notify ("VT ACTIVATE VT=%d", active_vt);
520             }
521             break;
522         case VT_WAITACTIVE:
523             break;
524         }
525         return 0;
526     }
527     else
528     {
529         va_list ap;
530         void *data;
531
532         va_start (ap, request);
533         data = va_arg (ap, void *);
534         va_end (ap);
535         return _ioctl (d, request, data);
536     }
537 }
538
539 int
540 close (int fd)
541 {
542     int (*_close) (int fd);
543
544     if (fd > 0 && fd == console_fd)
545         return 0;
546
547     _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
548     return _close (fd);
549 }
550 #endif
551
552 static void
553 free_user (gpointer data)
554 {
555     struct passwd *entry = data;
556   
557     g_free (entry->pw_name);
558     g_free (entry->pw_passwd);
559     g_free (entry->pw_gecos);
560     g_free (entry->pw_dir);
561     g_free (entry->pw_shell);
562     g_free (entry);
563 }
564
565 static void
566 load_passwd_file (void)
567 {
568     gchar *path, *data = NULL, **lines;
569     gint i;
570     GError *error = NULL;
571
572     g_list_free_full (user_entries, free_user);
573     user_entries = NULL;
574     getpwent_link = NULL;
575
576     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
577     g_file_get_contents (path, &data, NULL, &error);
578     g_free (path);
579     if (error)
580         g_warning ("Error loading passwd file: %s", error->message);
581     g_clear_error (&error);
582
583     if (!data)
584         return;
585
586     lines = g_strsplit (data, "\n", -1);
587     g_free (data);
588
589     for (i = 0; lines[i]; i++)
590     {
591         gchar *line, **fields;
592
593         line = g_strstrip (lines[i]);
594         fields = g_strsplit (line, ":", -1);
595         if (g_strv_length (fields) == 7)
596         {
597             struct passwd *entry = malloc (sizeof (struct passwd));
598
599             entry->pw_name = g_strdup (fields[0]);
600             entry->pw_passwd = g_strdup (fields[1]);
601             entry->pw_uid = atoi (fields[2]);
602             entry->pw_gid = atoi (fields[3]);
603             entry->pw_gecos = g_strdup (fields[4]);
604             entry->pw_dir = g_strdup (fields[5]);
605             entry->pw_shell = g_strdup (fields[6]);
606             user_entries = g_list_append (user_entries, entry);
607         }
608         g_strfreev (fields);
609     }
610     g_strfreev (lines);
611 }
612
613 struct passwd *
614 getpwent (void)
615 {
616     if (getpwent_link == NULL)
617     {
618         load_passwd_file ();
619         if (user_entries == NULL)
620             return NULL;
621         getpwent_link = user_entries;
622     }
623     else
624     {
625         if (getpwent_link->next == NULL)
626             return NULL;
627         getpwent_link = getpwent_link->next;
628     }
629
630     return getpwent_link->data;
631 }
632
633 void
634 setpwent (void)
635 {
636     getpwent_link = NULL;
637 }
638
639 void
640 endpwent (void)
641 {
642     getpwent_link = NULL;
643 }
644
645 struct passwd *
646 getpwnam (const char *name)
647 {
648     GList *link;
649   
650     if (name == NULL)
651         return NULL;
652   
653     load_passwd_file ();
654
655     for (link = user_entries; link; link = link->next)
656     {
657         struct passwd *entry = link->data;
658         if (strcmp (entry->pw_name, name) == 0)
659             break;
660     }
661     if (!link)
662         return NULL;
663
664     return link->data;
665 }
666
667 struct passwd *
668 getpwuid (uid_t uid)
669 {
670     GList *link;
671
672     load_passwd_file ();
673
674     for (link = user_entries; link; link = link->next)
675     {
676         struct passwd *entry = link->data;
677         if (entry->pw_uid == uid)
678             break;
679     }
680     if (!link)
681         return NULL;
682
683     return link->data;
684 }
685
686 static void
687 free_group (gpointer data)
688 {
689     struct group *entry = data;
690   
691     g_free (entry->gr_name);
692     g_free (entry->gr_passwd);
693     g_strfreev (entry->gr_mem);
694     g_free (entry);
695 }
696
697 static void
698 load_group_file (void)
699 {
700     gchar *path, *data = NULL, **lines;
701     gint i;
702     GError *error = NULL;
703
704     g_list_free_full (group_entries, free_group);
705     group_entries = NULL;
706
707     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
708     g_file_get_contents (path, &data, NULL, &error);
709     g_free (path);
710     if (error)
711         g_warning ("Error loading group file: %s", error->message);
712     g_clear_error (&error);
713
714     if (!data)
715         return;
716
717     lines = g_strsplit (data, "\n", -1);
718     g_free (data);
719
720     for (i = 0; lines[i]; i++)
721     {
722         gchar *line, **fields;
723
724         line = g_strstrip (lines[i]);
725         fields = g_strsplit (line, ":", -1);
726         if (g_strv_length (fields) == 4)
727         {
728             struct group *entry = malloc (sizeof (struct group));
729
730             entry->gr_name = g_strdup (fields[0]);
731             entry->gr_passwd = g_strdup (fields[1]);
732             entry->gr_gid = atoi (fields[2]);
733             entry->gr_mem = g_strsplit (fields[3], ",", -1);
734             group_entries = g_list_append (group_entries, entry);
735         }
736         g_strfreev (fields);
737     }
738     g_strfreev (lines);
739 }
740
741 struct group *
742 getgrnam (const char *name)
743 {
744     GList *link;
745
746     load_group_file ();
747
748     for (link = group_entries; link; link = link->next)
749     {
750         struct group *entry = link->data;
751         if (strcmp (entry->gr_name, name) == 0)
752             break;
753     }
754     if (!link)
755         return NULL;
756
757     return link->data;
758 }
759
760 struct group *
761 getgrgid (gid_t gid)
762 {
763     GList *link;
764
765     load_group_file ();
766
767     for (link = group_entries; link; link = link->next)
768     {
769         struct group *entry = link->data;
770         if (entry->gr_gid == gid)
771             break;
772     }
773     if (!link)
774         return NULL;
775
776     return link->data;
777 }
778
779 int
780 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
781 {
782     pam_handle_t *handle;
783
784     if (service_name == NULL || conversation == NULL || pamh == NULL)
785         return PAM_SYSTEM_ERR;
786
787     handle = *pamh = malloc (sizeof (pam_handle_t));
788     if (handle == NULL)
789         return PAM_BUF_ERR;
790
791     handle->service_name = strdup (service_name);
792     handle->user = user ? strdup (user) : NULL;
793     handle->authtok = NULL;
794     handle->ruser = NULL;
795     handle->tty = NULL;
796     handle->conversation.conv = conversation->conv;
797     handle->conversation.appdata_ptr = conversation->appdata_ptr;
798     handle->envlist = malloc (sizeof (char *) * 1);
799     handle->envlist[0] = NULL;
800
801     return PAM_SUCCESS;
802 }
803
804 static void
805 send_info (pam_handle_t *pamh, const char *message)
806 {
807     struct pam_message **msg;
808     struct pam_response *resp = NULL;
809
810     msg = calloc (1, sizeof (struct pam_message *));
811     msg[0] = malloc (sizeof (struct pam_message));
812     msg[0]->msg_style = PAM_TEXT_INFO;
813     msg[0]->msg = message;
814     pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
815     free (msg[0]);
816     free (msg);
817     if (resp)
818     {
819         if (resp[0].resp)
820             free (resp[0].resp);
821         free (resp);
822     }
823 }
824
825 int
826 pam_authenticate (pam_handle_t *pamh, int flags)
827 {
828     struct passwd *entry;
829     gboolean password_matches = FALSE;
830
831     if (pamh == NULL)
832         return PAM_SYSTEM_ERR;
833   
834     if (strcmp (pamh->service_name, "test-remote") == 0)
835     {
836         int result;
837         struct pam_message **msg;
838         struct pam_response *resp = NULL;
839
840         msg = malloc (sizeof (struct pam_message *) * 1);
841         msg[0] = malloc (sizeof (struct pam_message));
842         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
843         msg[0]->msg = "remote-login:";
844         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
845         free (msg[0]);
846         free (msg);
847         if (result != PAM_SUCCESS)
848             return result;
849
850         if (resp == NULL)
851             return PAM_CONV_ERR;
852         if (resp[0].resp == NULL)
853         {
854             free (resp);
855             return PAM_CONV_ERR;
856         }
857
858         if (pamh->ruser)
859             free (pamh->ruser);
860         pamh->ruser = strdup (resp[0].resp);
861         free (resp[0].resp);
862         free (resp);
863
864         msg = malloc (sizeof (struct pam_message *) * 1);
865         msg[0] = malloc (sizeof (struct pam_message));
866         msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
867         msg[0]->msg = "remote-password:";
868         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
869         free (msg[0]);
870         free (msg);
871         if (result != PAM_SUCCESS)
872             return result;
873
874         if (resp == NULL)
875             return PAM_CONV_ERR;
876         if (resp[0].resp == NULL)
877         {
878             free (resp);
879             return PAM_CONV_ERR;
880         }
881
882         if (pamh->authtok)
883             free (pamh->authtok);
884         pamh->authtok = strdup (resp[0].resp);
885         free (resp[0].resp);
886         free (resp);
887
888         password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
889
890         if (password_matches)
891             return PAM_SUCCESS;
892         else
893             return PAM_AUTH_ERR;
894     }
895
896     /* Prompt for username */
897     if (pamh->user == NULL)
898     {
899         int result;
900         struct pam_message **msg;
901         struct pam_response *resp = NULL;
902
903         msg = malloc (sizeof (struct pam_message *) * 1);
904         msg[0] = malloc (sizeof (struct pam_message));
905         msg[0]->msg_style = PAM_PROMPT_ECHO_ON; 
906         msg[0]->msg = LOGIN_PROMPT;
907         result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
908         free (msg[0]);
909         free (msg);
910         if (result != PAM_SUCCESS)
911             return result;
912
913         if (resp == NULL)
914             return PAM_CONV_ERR;
915         if (resp[0].resp == NULL)
916         {
917             free (resp);
918             return PAM_CONV_ERR;
919         }
920       
921         pamh->user = strdup (resp[0].resp);
922         free (resp[0].resp);
923         free (resp);
924     }
925
926     if (strcmp (pamh->user, "log-pam") == 0)
927         send_info (pamh, "pam_authenticate");
928
929     /* Crash on authenticate */
930     if (strcmp (pamh->user, "crash-authenticate") == 0)
931         kill (getpid (), SIGSEGV);
932
933     /* Look up password database */
934     entry = getpwnam (pamh->user);
935
936     /* Prompt for password if required */
937     if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
938         password_matches = TRUE;
939     else
940     {
941         int i, n_messages = 0, password_index, result;
942         struct pam_message **msg;
943         struct pam_response *resp = NULL;
944
945         msg = malloc (sizeof (struct pam_message *) * 5);
946         if (strcmp (pamh->user, "info-prompt") == 0)
947         {
948             msg[n_messages] = malloc (sizeof (struct pam_message));
949             msg[n_messages]->msg_style = PAM_TEXT_INFO;
950             msg[n_messages]->msg = "Welcome to LightDM";
951             n_messages++;
952         }
953         if (strcmp (pamh->user, "multi-info-prompt") == 0)
954         {
955             msg[n_messages] = malloc (sizeof (struct pam_message));
956             msg[n_messages]->msg_style = PAM_TEXT_INFO;
957             msg[n_messages]->msg = "Welcome to LightDM";
958             n_messages++;
959             msg[n_messages] = malloc (sizeof (struct pam_message));
960             msg[n_messages]->msg_style = PAM_ERROR_MSG;
961             msg[n_messages]->msg = "This is an error";
962             n_messages++;
963             msg[n_messages] = malloc (sizeof (struct pam_message));
964             msg[n_messages]->msg_style = PAM_TEXT_INFO;
965             msg[n_messages]->msg = "You should have seen three messages";
966             n_messages++;
967         }
968         if (strcmp (pamh->user, "multi-prompt") == 0)
969         {
970             msg[n_messages] = malloc (sizeof (struct pam_message));
971             msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
972             msg[n_messages]->msg = "Favorite Color:";
973             n_messages++;
974         }
975         msg[n_messages] = malloc (sizeof (struct pam_message));
976         msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
977         msg[n_messages]->msg = "Password:";
978         password_index = n_messages;
979         n_messages++;
980         result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
981         for (i = 0; i < n_messages; i++)
982             free (msg[i]);
983         free (msg);
984         if (result != PAM_SUCCESS)
985             return result;
986
987         if (resp == NULL)
988             return PAM_CONV_ERR;
989         if (resp[password_index].resp == NULL)
990         {
991             free (resp);
992             return PAM_CONV_ERR;
993         }
994
995         if (entry)
996             password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
997
998         if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
999             password_matches = strcmp ("blue", resp[0].resp) == 0;
1000
1001         for (i = 0; i < n_messages; i++)
1002         {
1003             if (resp[i].resp)
1004                 free (resp[i].resp);
1005         }
1006         free (resp);
1007
1008         /* Do two factor authentication */
1009         if (password_matches && strcmp (pamh->user, "two-factor") == 0)
1010         {
1011             msg = malloc (sizeof (struct pam_message *) * 1);
1012             msg[0] = malloc (sizeof (struct pam_message));
1013             msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
1014             msg[0]->msg = "OTP:";
1015             resp = NULL;
1016             result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1017             free (msg[0]);
1018             free (msg);
1019
1020             if (resp == NULL)
1021                 return PAM_CONV_ERR;
1022             if (resp[0].resp == NULL)
1023             {
1024                 free (resp);
1025                 return PAM_CONV_ERR;
1026             }
1027             password_matches = strcmp (resp[0].resp, "otp") == 0;
1028             free (resp[0].resp);
1029             free (resp);
1030         }
1031     }
1032
1033     /* Special user has home directory created on login */
1034     if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
1035         g_mkdir_with_parents (entry->pw_dir, 0755);
1036
1037     /* Special user 'change-user1' changes user on authentication */
1038     if (password_matches && strcmp (pamh->user, "change-user1") == 0)
1039     {
1040         g_free (pamh->user);
1041         pamh->user = g_strdup ("change-user2");
1042     }
1043
1044     /* Special user 'change-user-invalid' changes to an invalid user on authentication */
1045     if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
1046     {
1047         g_free (pamh->user);
1048         pamh->user = g_strdup ("invalid-user");
1049     }
1050
1051     if (password_matches)
1052         return PAM_SUCCESS;
1053     else
1054         return PAM_AUTH_ERR;
1055 }
1056
1057 static const char *
1058 get_env_value (const char *name_value, const char *name)
1059 {
1060     int j;
1061   
1062     for (j = 0; name[j] && name_value[j] && name[j] == name_value[j]; j++);
1063     if (name[j] == '\0' && name_value[j] == '=')
1064         return &name_value[j + 1];
1065
1066     return NULL;
1067 }
1068
1069 int
1070 pam_putenv (pam_handle_t *pamh, const char *name_value)
1071 {
1072     int i;
1073     gchar *name;
1074
1075     if (pamh == NULL || name_value == NULL)
1076         return PAM_SYSTEM_ERR;
1077
1078     name = strdup (name_value);
1079     for (i = 0; name[i]; i++)
1080         if (name[i] == '=')
1081             name[i] = '\0';
1082     for (i = 0; pamh->envlist[i]; i++)
1083     {
1084         if (get_env_value (pamh->envlist[i], name))
1085             break;
1086     }
1087     free (name);
1088
1089     if (pamh->envlist[i])
1090     {
1091         free (pamh->envlist[i]);
1092         pamh->envlist[i] = strdup (name_value);
1093     }
1094     else
1095     {
1096         pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
1097         pamh->envlist[i] = strdup (name_value);
1098         pamh->envlist[i + 1] = NULL;
1099     }
1100
1101     return PAM_SUCCESS;
1102 }
1103
1104 const char *
1105 pam_getenv (pam_handle_t *pamh, const char *name)
1106 {
1107     int i;
1108
1109     if (pamh == NULL || name == NULL)
1110         return NULL;
1111
1112     for (i = 0; pamh->envlist[i]; i++)
1113     {
1114         const char *value;
1115         value = get_env_value (pamh->envlist[i], name);
1116         if (value)
1117             return value;
1118     }
1119
1120     return NULL;
1121 }
1122
1123 char **
1124 pam_getenvlist (pam_handle_t *pamh)
1125 {
1126     if (pamh == NULL)
1127         return NULL;
1128
1129     return pamh->envlist;
1130 }
1131
1132 int
1133 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
1134 {
1135     if (pamh == NULL || item == NULL)
1136         return PAM_SYSTEM_ERR;
1137
1138     switch (item_type)
1139     {
1140     case PAM_TTY:
1141         if (pamh->tty)
1142             free (pamh->tty);
1143         pamh->tty = strdup ((const char *) item);
1144         return PAM_SUCCESS;
1145
1146     default:
1147         return PAM_BAD_ITEM;
1148     }
1149 }
1150
1151 int
1152 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
1153 {
1154     if (pamh == NULL || item == NULL)
1155         return PAM_SYSTEM_ERR;
1156   
1157     switch (item_type)
1158     {
1159     case PAM_SERVICE:
1160         *item = pamh->service_name;
1161         return PAM_SUCCESS;
1162       
1163     case PAM_USER:
1164         *item = pamh->user;
1165         return PAM_SUCCESS;
1166
1167     case PAM_AUTHTOK:
1168         *item = pamh->authtok;
1169         return PAM_SUCCESS;
1170
1171     case PAM_RUSER:
1172         *item = pamh->ruser;
1173         return PAM_SUCCESS;
1174      
1175     case PAM_USER_PROMPT:
1176         *item = LOGIN_PROMPT;
1177         return PAM_SUCCESS;
1178       
1179     case PAM_TTY:
1180         *item = pamh->tty;
1181         return PAM_SUCCESS;
1182
1183     case PAM_CONV:
1184         *item = &pamh->conversation;
1185         return PAM_SUCCESS;
1186
1187     default:
1188         return PAM_BAD_ITEM;
1189     }
1190 }
1191
1192 int
1193 pam_open_session (pam_handle_t *pamh, int flags)
1194 {
1195     if (pamh == NULL)
1196         return PAM_SYSTEM_ERR;
1197
1198     if (strcmp (pamh->user, "session-error") == 0)
1199         return PAM_SESSION_ERR;
1200
1201     if (strcmp (pamh->user, "log-pam") == 0)
1202         send_info (pamh, "pam_open_session");
1203
1204     if (strcmp (pamh->user, "make-home-dir") == 0)
1205     {
1206         struct passwd *entry;
1207         entry = getpwnam (pamh->user);
1208         g_mkdir_with_parents (entry->pw_dir, 0755);
1209     }
1210
1211     return PAM_SUCCESS;
1212 }
1213
1214 int
1215 pam_close_session (pam_handle_t *pamh, int flags)
1216 {
1217     if (pamh == NULL)
1218         return PAM_SYSTEM_ERR;
1219
1220     if (strcmp (pamh->user, "log-pam") == 0)
1221         send_info (pamh, "pam_close_session");
1222
1223     return PAM_SUCCESS;
1224 }
1225
1226 int
1227 pam_acct_mgmt (pam_handle_t *pamh, int flags)
1228 {
1229     if (pamh == NULL)
1230         return PAM_SYSTEM_ERR;
1231   
1232     if (!pamh->user)
1233         return PAM_USER_UNKNOWN;
1234
1235     if (strcmp (pamh->user, "log-pam") == 0)
1236         send_info (pamh, "pam_acct_mgmt");
1237
1238     if (strcmp (pamh->user, "denied") == 0)
1239         return PAM_PERM_DENIED;
1240     if (strcmp (pamh->user, "expired") == 0)
1241         return PAM_ACCT_EXPIRED;
1242     if (strcmp (pamh->user, "new-authtok") == 0)
1243         return PAM_NEW_AUTHTOK_REQD;
1244
1245     return PAM_SUCCESS;
1246 }
1247
1248 int
1249 pam_chauthtok (pam_handle_t *pamh, int flags)
1250 {
1251     struct passwd *entry;
1252     int result;
1253     struct pam_message **msg;
1254     struct pam_response *resp = NULL;
1255
1256     if (pamh == NULL)
1257         return PAM_SYSTEM_ERR;
1258
1259     if (strcmp (pamh->user, "log-pam") == 0)
1260         send_info (pamh, "pam_chauthtok");
1261
1262     msg = malloc (sizeof (struct pam_message *) * 1);
1263     msg[0] = malloc (sizeof (struct pam_message));
1264     msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
1265     if ((flags & PAM_CHANGE_EXPIRED_AUTHTOK) != 0)
1266         msg[0]->msg = "Enter new password (expired):";
1267     else
1268         msg[0]->msg = "Enter new password:";
1269     result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
1270     free (msg[0]);
1271     free (msg);
1272     if (result != PAM_SUCCESS)
1273         return result;
1274
1275     if (resp == NULL)
1276         return PAM_CONV_ERR;
1277     if (resp[0].resp == NULL)
1278     {
1279         free (resp);
1280         return PAM_CONV_ERR;
1281     }
1282
1283     /* Update password database */
1284     entry = getpwnam (pamh->user);
1285     free (entry->pw_passwd);
1286     entry->pw_passwd = resp[0].resp;
1287     free (resp);
1288
1289     return PAM_SUCCESS;
1290 }
1291
1292 int
1293 pam_setcred (pam_handle_t *pamh, int flags)
1294 {
1295     gchar *e;
1296
1297     if (pamh == NULL)
1298         return PAM_SYSTEM_ERR;
1299
1300     if (strcmp (pamh->user, "log-pam") == 0)
1301         send_info (pamh, "pam_setcred");
1302
1303     /* Put the test directories into the path */
1304     e = g_strdup_printf ("PATH=%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s/src:%s", BUILDDIR, BUILDDIR, SRCDIR, BUILDDIR, pam_getenv (pamh, "PATH"));
1305     pam_putenv (pamh, e);
1306     g_free (e);
1307
1308     if (strcmp (pamh->user, "cred-error") == 0)
1309         return PAM_CRED_ERR;
1310     if (strcmp (pamh->user, "cred-expired") == 0)
1311         return PAM_CRED_EXPIRED;
1312     if (strcmp (pamh->user, "cred-unavail") == 0)
1313         return PAM_CRED_UNAVAIL;
1314
1315     /* Join special groups if requested */
1316     if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1317     {
1318         struct group *group;
1319         gid_t *groups;
1320         int groups_length;
1321
1322         group = getgrnam ("test-group");
1323         if (group)
1324         {
1325             groups_length = getgroups (0, NULL);
1326             if (groups_length < 0)
1327                 return PAM_SYSTEM_ERR;
1328             groups = malloc (sizeof (gid_t) * (groups_length + 1));
1329             groups_length = getgroups (groups_length, groups);
1330             if (groups_length < 0)
1331                 return PAM_SYSTEM_ERR;
1332             groups[groups_length] = group->gr_gid;
1333             groups_length++;
1334             setgroups (groups_length, groups);
1335             free (groups);
1336         }
1337
1338         /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1339         pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1340     }
1341
1342     return PAM_SUCCESS;
1343 }
1344
1345 int
1346 pam_end (pam_handle_t *pamh, int pam_status)
1347 {
1348     if (pamh == NULL)
1349         return PAM_SYSTEM_ERR;
1350   
1351     free (pamh->service_name);
1352     if (pamh->user)
1353         free (pamh->user);
1354     if (pamh->authtok)
1355         free (pamh->authtok);
1356     if (pamh->ruser)
1357         free (pamh->ruser);
1358     if (pamh->tty)
1359         free (pamh->tty);
1360     free (pamh);
1361
1362     return PAM_SUCCESS;
1363 }
1364
1365 const char *
1366 pam_strerror (pam_handle_t *pamh, int errnum)
1367 {
1368     if (pamh == NULL)
1369         return NULL;
1370
1371     switch (errnum)
1372     {
1373     case PAM_SUCCESS:
1374         return "Success";
1375     case PAM_ABORT:
1376         return "Critical error - immediate abort";
1377     case PAM_OPEN_ERR:
1378         return "Failed to load module";
1379     case PAM_SYMBOL_ERR:
1380         return "Symbol not found";
1381     case PAM_SERVICE_ERR:
1382         return "Error in service module";
1383     case PAM_SYSTEM_ERR:
1384         return "System error";
1385     case PAM_BUF_ERR:
1386         return "Memory buffer error";
1387     case PAM_PERM_DENIED:
1388         return "Permission denied";
1389     case PAM_AUTH_ERR:
1390         return "Authentication failure";
1391     case PAM_CRED_INSUFFICIENT:
1392         return "Insufficient credentials to access authentication data";
1393     case PAM_AUTHINFO_UNAVAIL:
1394         return "Authentication service cannot retrieve authentication info";
1395     case PAM_USER_UNKNOWN:
1396         return "User not known to the underlying authentication module";
1397     case PAM_MAXTRIES:
1398         return "Have exhausted maximum number of retries for service";
1399     case PAM_NEW_AUTHTOK_REQD:
1400         return "Authentication token is no longer valid; new one required";
1401     case PAM_ACCT_EXPIRED:
1402         return "User account has expired";
1403     case PAM_SESSION_ERR:
1404         return "Cannot make/remove an entry for the specified session";
1405     case PAM_CRED_UNAVAIL:
1406         return "Authentication service cannot retrieve user credentials";
1407     case PAM_CRED_EXPIRED:
1408         return "User credentials expired";
1409     case PAM_CRED_ERR:
1410         return "Failure setting user credentials";
1411     case PAM_NO_MODULE_DATA:
1412         return "No module specific data is present";
1413     case PAM_BAD_ITEM:
1414         return "Bad item passed to pam_*_item()";
1415     case PAM_CONV_ERR:
1416         return "Conversation error";
1417     case PAM_AUTHTOK_ERR:
1418         return "Authentication token manipulation error";
1419     case PAM_AUTHTOK_RECOVERY_ERR:
1420         return "Authentication information cannot be recovered";
1421     case PAM_AUTHTOK_LOCK_BUSY:
1422         return "Authentication token lock busy";
1423     case PAM_AUTHTOK_DISABLE_AGING:
1424         return "Authentication token aging disabled";
1425     case PAM_TRY_AGAIN:
1426         return "Failed preliminary check by password service";
1427     case PAM_IGNORE:
1428         return "The return value should be ignored by PAM dispatch";
1429     case PAM_MODULE_UNKNOWN:
1430         return "Module is unknown";
1431     case PAM_AUTHTOK_EXPIRED:
1432         return "Authentication token expired";
1433     case PAM_CONV_AGAIN:
1434         return "Conversation is waiting for event";
1435     case PAM_INCOMPLETE:
1436         return "Application needs to call libpam again";
1437     default:
1438         return "Unknown PAM error";
1439     }
1440 }
1441
1442 void
1443 setutxent (void)
1444 {
1445 }
1446
1447 struct utmpx *
1448 pututxline (const struct utmpx *ut)
1449 {
1450     connect_status ();
1451     if (g_key_file_get_boolean (config, "test-utmp-config", "check-events", NULL))
1452     {
1453         GString *status;
1454
1455         status = g_string_new ("UTMP");
1456         switch (ut->ut_type)
1457         {
1458         case INIT_PROCESS:
1459             g_string_append_printf (status, " TYPE=INIT_PROCESS");
1460             break;
1461         case LOGIN_PROCESS:
1462             g_string_append_printf (status, " TYPE=LOGIN_PROCESS");
1463             break;
1464         case USER_PROCESS:
1465             g_string_append_printf (status, " TYPE=USER_PROCESS");
1466             break;
1467         case DEAD_PROCESS:
1468             g_string_append_printf (status, " TYPE=DEAD_PROCESS");
1469             break;
1470         default:
1471             g_string_append_printf (status, " TYPE=%d", ut->ut_type);
1472         }
1473         if (ut->ut_line)
1474             g_string_append_printf (status, " LINE=%s", ut->ut_line);
1475         if (ut->ut_id)
1476             g_string_append_printf (status, " ID=%s", ut->ut_id);
1477         if (ut->ut_user)
1478             g_string_append_printf (status, " USER=%s", ut->ut_user);
1479         if (ut->ut_host)
1480             g_string_append_printf (status, " HOST=%s", ut->ut_host);
1481         status_notify ("%s", status->str);
1482         g_string_free (status, TRUE);
1483     }
1484
1485     return (struct utmpx *)ut;
1486 }
1487
1488 void
1489 endutxent (void)
1490 {
1491 }
1492
1493 void
1494 updwtmp (const char *wtmp_file, const struct utmp *ut)
1495 {
1496     connect_status ();
1497     if (g_key_file_get_boolean (config, "test-utmp-config", "check-events", NULL))
1498     {
1499         GString *status;
1500
1501         status = g_string_new ("WTMP");
1502         g_string_append_printf (status, " FILE=%s", wtmp_file);
1503         switch (ut->ut_type)
1504         {
1505         case INIT_PROCESS:
1506             g_string_append_printf (status, " TYPE=INIT_PROCESS");
1507             break;
1508         case LOGIN_PROCESS:
1509             g_string_append_printf (status, " TYPE=LOGIN_PROCESS");
1510             break;
1511         case USER_PROCESS:
1512             g_string_append_printf (status, " TYPE=USER_PROCESS");
1513             break;
1514         case DEAD_PROCESS:
1515             g_string_append_printf (status, " TYPE=DEAD_PROCESS");
1516             break;
1517         default:
1518             g_string_append_printf (status, " TYPE=%d", ut->ut_type);
1519         }
1520         if (ut->ut_line)
1521             g_string_append_printf (status, " LINE=%s", ut->ut_line);
1522         if (ut->ut_id)
1523             g_string_append_printf (status, " ID=%s", ut->ut_id);
1524         if (ut->ut_user)
1525             g_string_append_printf (status, " USER=%s", ut->ut_user);
1526         if (ut->ut_host)
1527             g_string_append_printf (status, " HOST=%s", ut->ut_host);
1528         status_notify ("%s", status->str);
1529         g_string_free (status, TRUE);
1530     }
1531 }
1532
1533 struct xcb_connection_t
1534 {
1535     gchar *display;
1536     int error;
1537     GSocket *socket;
1538 };
1539
1540 xcb_connection_t *
1541 xcb_connect_to_display_with_auth_info (const char *display, xcb_auth_info_t *auth, int *screen)
1542 {
1543     xcb_connection_t *c;
1544     gchar *socket_path;
1545     GError *error = NULL;
1546   
1547     c = malloc (sizeof (xcb_connection_t));
1548     c->display = g_strdup (display);
1549     c->error = 0;
1550
1551     if (display == NULL)
1552         display = getenv ("DISPLAY");
1553     if (display == NULL)
1554         c->error = XCB_CONN_CLOSED_PARSE_ERR;
1555
1556     if (c->error == 0)
1557     {
1558         c->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
1559         if (error)
1560             g_printerr ("%s\n", error->message);
1561         g_clear_error (&error);
1562         if (c->socket == NULL)
1563             c->error = XCB_CONN_ERROR;
1564     }
1565
1566     if (c->error == 0)
1567     {
1568         gchar *d;
1569         GSocketAddress *address;
1570
1571         /* Skip the hostname, we'll assume it's localhost */
1572         d = g_strdup_printf (".x%s", strchr (display, ':'));
1573
1574         socket_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), d, NULL);
1575         g_free (d);
1576         address = g_unix_socket_address_new (socket_path);
1577         if (!g_socket_connect (c->socket, address, NULL, &error))
1578             c->error = XCB_CONN_ERROR;
1579         g_object_unref (address);
1580         if (error)
1581             g_printerr ("Failed to connect to X socket %s: %s\n", socket_path, error->message);
1582         g_free (socket_path);
1583         g_clear_error (&error);
1584     }
1585
1586     // FIXME: Send auth info
1587     if (c->error == 0)
1588     {
1589     }
1590
1591     return c;
1592 }
1593
1594 xcb_connection_t *
1595 xcb_connect (const char *displayname, int *screenp)
1596 {
1597     return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
1598 }
1599
1600 int
1601 xcb_connection_has_error (xcb_connection_t *c)
1602 {
1603     return c->error;
1604 }
1605
1606 void
1607 xcb_disconnect (xcb_connection_t *c)
1608 {
1609     free (c->display);
1610     if (c->socket)
1611         g_object_unref (c->socket);
1612     free (c);
1613 }