2003-04-14 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-userdb.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-userdb.c User database abstraction
3  * 
4  * Copyright (C) 2003  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-userdb.h"
24 #include "dbus-hash.h"
25 #include "dbus-test.h"
26 #include "dbus-internals.h"
27 #include <string.h>
28
29 typedef struct DBusUserEntry DBusUserEntry;
30
31 struct DBusUserEntry
32 {
33   dbus_uid_t  uid;
34
35   dbus_gid_t *group_ids;
36   int         n_group_ids;
37 };
38
39 struct DBusUserDatabase
40 {
41   int refcount;
42
43   DBusHashTable *users;
44 };
45
46 static void
47 free_user_entry (void *data)
48 {
49   DBusUserEntry *entry = data;
50
51   if (entry == NULL) /* hash table will pass NULL */
52     return;
53
54   dbus_free (entry->group_ids);
55   
56   dbus_free (entry);
57 }
58
59 static DBusUserEntry*
60 _dbus_user_database_lookup (DBusUserDatabase *db,
61                             dbus_uid_t        uid,
62                             DBusError        *error)
63 {
64   DBusUserEntry *entry;
65
66   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
67   
68   entry = _dbus_hash_table_lookup_ulong (db->users, uid);
69   if (entry)
70     return entry;
71   else
72     {
73       entry = dbus_new0 (DBusUserEntry, 1);
74       if (entry == NULL)
75         {
76           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
77           return NULL;
78         }
79
80       if (!_dbus_get_groups (uid, &entry->group_ids, &entry->n_group_ids, error))
81         {
82           _DBUS_ASSERT_ERROR_IS_SET (error);
83           free_user_entry (entry);
84           return NULL;
85         }
86
87       if (!_dbus_hash_table_insert_ulong (db->users, entry->uid, entry))
88         {
89           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
90           free_user_entry (entry);
91           return NULL;
92         }
93
94       return entry;
95     }
96 }
97
98 /**
99  * @addtogroup DBusInternalsUtils
100  * @{
101  */
102
103 /**
104  * Creates a new user database object used to look up and
105  * cache user information.
106  * @returns new database, or #NULL on out of memory
107  */
108 DBusUserDatabase*
109 _dbus_user_database_new (void)
110 {
111   DBusUserDatabase *db;
112   
113   db = dbus_new0 (DBusUserDatabase, 1);
114   if (db == NULL)
115     return NULL;
116
117   db->refcount = 1;
118
119   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
120                                     NULL, free_user_entry);
121
122   if (db->users == NULL)
123     goto failed;
124   
125   return db;
126   
127  failed:
128   _dbus_user_database_unref (db);
129   return NULL;
130 }
131
132 /**
133  * Increments refcount of user database.
134  * @param db the database
135  */
136 void
137 _dbus_user_database_ref (DBusUserDatabase  *db)
138 {
139   _dbus_assert (db->refcount > 0);
140
141   db->refcount += 1;
142 }
143
144 /**
145  * Decrements refcount of user database.
146  * @param db the database
147  */
148 void
149 _dbus_user_database_unref (DBusUserDatabase  *db)
150 {
151   _dbus_assert (db->refcount > 0);
152
153   db->refcount -= 1;
154   if (db->refcount == 0)
155     {
156       if (db->users)
157         _dbus_hash_table_unref (db->users);
158       
159       dbus_free (db);
160     }
161 }
162
163 /**
164  * Gets all groups for a particular user. Returns #FALSE
165  * if no memory, or user isn't known, but always initializes
166  * group_ids to a NULL array. Sets error to the reason
167  * for returning #FALSE.
168  *
169  * @param db the user database object
170  * @param uid the user ID
171  * @param group_ids return location for array of group IDs
172  * @param n_group_ids return location for length of returned array
173  * @param error return location for error
174  * @returns #TRUE on success
175  */
176 dbus_bool_t
177 _dbus_user_database_get_groups (DBusUserDatabase  *db,
178                                 dbus_uid_t         uid,
179                                 dbus_gid_t       **group_ids,
180                                 int               *n_group_ids,
181                                 DBusError         *error)
182 {
183   DBusUserEntry *entry;
184   
185   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
186
187   *group_ids = NULL;
188   *n_group_ids = 0;
189   
190   entry = _dbus_user_database_lookup (db, uid, error);
191   if (entry == NULL)
192     {
193       _DBUS_ASSERT_ERROR_IS_SET (error);
194       return FALSE;
195     }
196
197   if (entry->n_group_ids > 0)
198     {
199       *group_ids = dbus_new (dbus_gid_t, entry->n_group_ids);
200       if (*group_ids == NULL)
201         {
202           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
203           return FALSE;
204         }
205
206       *n_group_ids = entry->n_group_ids;
207
208       memcpy (*group_ids, entry->group_ids, entry->n_group_ids * sizeof (dbus_gid_t));
209     }
210
211   return TRUE;
212 }
213
214 /** @} */
215
216 #ifdef DBUS_BUILD_TESTS
217 /**
218  * Unit test for dbus-userdb.c.
219  * 
220  * @returns #TRUE on success.
221  */
222 dbus_bool_t
223 _dbus_userdb_test (const char *test_data_dir)
224 {
225
226   return TRUE;
227 }
228 #endif /* DBUS_BUILD_TESTS */