2003-02-12 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-auth-script.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-auth-script.c Test DBusAuth using a special script file (internal to D-BUS implementation)
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 <config.h>
24
25 #ifdef DBUS_BUILD_TESTS
26
27 #include "dbus-auth-script.h"
28 #include "dbus-auth.h"
29 #include "dbus-string.h"
30 #include "dbus-hash.h"
31 #include "dbus-internals.h"
32 #include "dbus-marshal.h"
33
34 /**
35  * @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
36  * @ingroup  DBusInternals
37  * @brief DBusAuth unit test scripting
38  *
39  * The code in here is used for unit testing, it loads
40  * up a script that tests DBusAuth.
41  *
42  * @{
43  */
44
45 static dbus_bool_t
46 append_quoted_string (DBusString       *dest,
47                       const DBusString *quoted)
48 {
49   dbus_bool_t in_quotes = FALSE;
50   int i;
51
52   i = 0;
53   while (i < _dbus_string_get_length (quoted))
54     {
55       unsigned char b;
56
57       b = _dbus_string_get_byte (quoted, i);
58       
59       if (in_quotes)
60         {
61           if (b == '\'')
62             in_quotes = FALSE;
63           else
64             {
65               if (!_dbus_string_append_byte (dest, b))
66                 return FALSE;
67             }
68         }
69       else
70         {
71           if (b == '\'')
72             in_quotes = TRUE;
73           else if (b == ' ' || b == '\n' || b == '\t')
74             break; /* end on whitespace if not quoted */
75           else
76             {
77               if (!_dbus_string_append_byte (dest, b))
78                 return FALSE;
79             }
80         }
81       
82       ++i;
83     }
84
85   return TRUE;
86 }
87
88 static dbus_bool_t
89 same_first_word (const DBusString *a,
90                  const DBusString *b)
91 {
92   int first_a_blank, first_b_blank;
93
94   _dbus_string_find_blank (a, 0, &first_a_blank);
95   _dbus_string_find_blank (b, 0, &first_b_blank);
96
97   if (first_a_blank != first_b_blank)
98     return FALSE;
99
100   return _dbus_string_equal_len (a, b, first_a_blank);
101 }
102
103 static DBusAuthState
104 auth_state_from_string (const DBusString *str)
105
106   if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT"))
107     return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
108   else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY"))
109     return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
110   else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND"))
111     return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
112   else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT"))
113     return DBUS_AUTH_STATE_NEED_DISCONNECT;
114   else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED_WITH_UNUSED_BYTES"))
115     return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
116   else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
117     return DBUS_AUTH_STATE_AUTHENTICATED;
118   else
119     return -1;
120 }
121
122 static const char*
123 auth_state_to_string (DBusAuthState state)
124 {
125   switch (state)
126     {
127     case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
128       return "WAITING_FOR_INPUT";
129     case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
130       return "WAITING_FOR_MEMORY";
131     case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
132       return "HAVE_BYTES_TO_SEND";
133     case DBUS_AUTH_STATE_NEED_DISCONNECT:
134       return "NEED_DISCONNECT";
135     case DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES:
136       return "AUTHENTICATED_WITH_UNUSED_BYTES";
137     case DBUS_AUTH_STATE_AUTHENTICATED:
138       return "AUTHENTICATED";
139     }
140
141   return "unknown";
142 }
143
144 dbus_bool_t
145 _dbus_auth_script_run (const DBusString *filename)
146 {
147   DBusString file;
148   DBusResultCode result;
149   DBusString line;
150   dbus_bool_t retval;
151   int line_no;
152   DBusAuth *auth;
153   DBusString from_auth;
154   DBusAuthState state;
155   
156   retval = FALSE;
157   auth = NULL;
158   
159   if (!_dbus_string_init (&file, _DBUS_INT_MAX))
160     return FALSE;
161
162   if (!_dbus_string_init (&line, _DBUS_INT_MAX))
163     {
164       _dbus_string_free (&file);
165       return FALSE;
166     }
167
168   if (!_dbus_string_init (&from_auth, _DBUS_INT_MAX))
169     {
170       _dbus_string_free (&file);
171       _dbus_string_free (&line);
172       return FALSE;
173     }
174   
175   if ((result = _dbus_file_get_contents (&file, filename)) != DBUS_RESULT_SUCCESS)
176     {
177       const char *s;
178       _dbus_string_get_const_data (filename, &s);
179       _dbus_warn ("Getting contents of %s failed: %s\n",
180                   s, dbus_result_to_string (result));
181                      
182       goto out;
183     }
184
185   state = DBUS_AUTH_STATE_NEED_DISCONNECT;
186   line_no = 0;
187  next_iteration:
188   while (_dbus_string_pop_line (&file, &line))
189     {      
190       line_no += 1;
191
192       _dbus_string_delete_leading_blanks (&line);
193
194       if (auth != NULL)
195         {
196           while ((state = _dbus_auth_do_work (auth)) ==
197                  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
198             {
199               const DBusString *tmp;
200               if (_dbus_auth_get_bytes_to_send (auth, &tmp))
201                 {
202                   int count = _dbus_string_get_length (tmp);
203
204                   if (_dbus_string_copy (tmp, 0, &from_auth,
205                                          _dbus_string_get_length (&from_auth)))
206                     _dbus_auth_bytes_sent (auth, count);
207                 }
208             }
209         }
210       
211       if (_dbus_string_get_length (&line) == 0)
212         {
213           /* empty line */
214           goto next_iteration;
215         }
216       else if (_dbus_string_starts_with_c_str (&line,
217                                                "#"))
218         {
219           /* Ignore this comment */
220           goto next_iteration;
221         }
222       else if (_dbus_string_starts_with_c_str (&line,
223                                                "CLIENT"))
224         {
225           if (auth != NULL)
226             {
227               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
228               goto out;
229             }
230
231           auth = _dbus_auth_client_new ();
232           if (auth == NULL)
233             {
234               _dbus_warn ("no memory to create DBusAuth\n");
235               goto out;
236             }
237         }
238       else if (_dbus_string_starts_with_c_str (&line,
239                                                "SERVER"))
240         {
241           if (auth != NULL)
242             {
243               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
244               goto out;
245             }
246
247           auth = _dbus_auth_server_new ();
248           if (auth == NULL)
249             {
250               _dbus_warn ("no memory to create DBusAuth\n");
251               goto out;
252             }
253         }
254       else if (auth == NULL)
255         {
256           _dbus_warn ("must specify CLIENT or SERVER\n");
257           goto out;
258
259         }
260       else if (_dbus_string_starts_with_c_str (&line,
261                                                "SEND"))
262         {
263           DBusString to_send;
264           
265           _dbus_string_delete_first_word (&line);
266
267           if (!_dbus_string_init (&to_send, _DBUS_INT_MAX))
268             {
269               _dbus_warn ("no memory to allocate string\n");
270               goto out;
271             }
272
273           if (!append_quoted_string (&to_send, &line))
274             {
275               _dbus_warn ("failed to append quoted string line %d\n",
276                           line_no);
277               _dbus_string_free (&to_send);
278               goto out;
279             }
280
281           {
282             const char *s4;
283             _dbus_string_get_const_data (&to_send, &s4);
284             _dbus_verbose ("Sending '%s'\n", s4);
285           }
286           
287           if (!_dbus_string_append (&to_send, "\r\n"))
288             {
289               _dbus_warn ("failed to append \r\n from line %d\n",
290                           line_no);
291               _dbus_string_free (&to_send);
292               goto out;
293             }
294           
295           if (!_dbus_auth_bytes_received (auth, &to_send))
296             {
297               _dbus_warn ("not enough memory to call bytes_received\n");
298               _dbus_string_free (&to_send);
299               goto out;
300             }
301
302           _dbus_string_free (&to_send);
303         }
304       else if (_dbus_string_starts_with_c_str (&line,
305                                                "EXPECT_STATE"))
306         {
307           DBusAuthState expected;
308           
309           _dbus_string_delete_first_word (&line);
310
311           expected = auth_state_from_string (&line);
312           if (expected < 0)
313             {
314               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
315               goto parse_failed;
316             }
317
318           if (expected != state)
319             {
320               _dbus_warn ("expected auth state %s but got %s on line %d\n",
321                           auth_state_to_string (expected),
322                           auth_state_to_string (state),
323                           line_no);
324               goto out;
325             }
326         }
327       else if (_dbus_string_starts_with_c_str (&line,
328                                                "EXPECT_COMMAND"))
329         {
330           DBusString received;
331           
332           _dbus_string_delete_first_word (&line);
333
334           if (!_dbus_string_init (&received, _DBUS_INT_MAX))
335             {
336               _dbus_warn ("no mem to allocate string received\n");
337               goto out;
338             }
339
340           if (!_dbus_string_pop_line (&from_auth, &received))
341             {
342               const char *command;
343               _dbus_string_get_const_data (&line, &command);
344               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
345                           command, line_no);
346               _dbus_string_free (&received);
347               goto out;
348             }
349
350           if (!same_first_word (&received, &line))
351             {
352               const char *s1, *s2;
353               _dbus_string_get_const_data (&line, &s1);
354               _dbus_string_get_const_data (&received, &s2);
355               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
356                           line_no, s1, s2);
357               _dbus_string_free (&received);
358               goto out;
359             }
360           
361           _dbus_string_free (&received);
362         }
363       else
364         goto parse_failed;
365
366       goto next_iteration; /* skip parse_failed */
367       
368     parse_failed:
369       {
370         const char *s;
371         _dbus_string_get_const_data (&line, &s);
372         _dbus_warn ("couldn't process line %d \"%s\"\n",
373                     line_no, s);
374         goto out;
375       }
376     }
377
378   if (auth != NULL &&
379       state == DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES)
380     {
381       _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
382       goto out;
383     }
384
385   if (_dbus_string_get_length (&from_auth) > 0)
386     {
387       const char *s;
388       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
389       _dbus_string_get_const_data (&from_auth, &s);
390       _dbus_warn ("Leftover data: %s\n", s);
391       goto out;
392     }
393   
394   retval = TRUE;
395   
396  out:
397   if (auth)
398     _dbus_auth_unref (auth);
399
400   _dbus_string_free (&file);
401   _dbus_string_free (&line);
402   _dbus_string_free (&from_auth);
403   
404   return retval;
405 }
406
407 /** @} */
408 #endif /* DBUS_BUILD_TESTS */