Git init
[profile/ivi/libsoup2.4.git] / tests / server-auth-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  */
5
6 #include "config.h"
7
8 #include <ctype.h>
9 #include <dirent.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 #include <glib.h>
20 #include <libsoup/soup-address.h>
21 #include <libsoup/soup-auth-domain-basic.h>
22 #include <libsoup/soup-auth-domain-digest.h>
23 #include <libsoup/soup-message.h>
24 #include <libsoup/soup-server.h>
25
26 #include "test-utils.h"
27
28 static struct {
29         gboolean client_sent_basic, client_sent_digest;
30         gboolean server_requested_basic, server_requested_digest;
31         gboolean succeeded;
32 } test_data;
33
34 static void
35 curl_exited (GPid pid, int status, gpointer data)
36 {
37         gboolean *done = data;
38
39         *done = TRUE;
40         test_data.succeeded = (status == 0);
41 }
42
43 static void
44 do_test (int n, SoupURI *base_uri, const char *path,
45          gboolean good_user, gboolean good_password,
46          gboolean offer_basic, gboolean offer_digest,
47          gboolean client_sends_basic, gboolean client_sends_digest,
48          gboolean server_requests_basic, gboolean server_requests_digest,
49          gboolean success)
50 {
51         SoupURI *uri;
52         char *uri_str;
53         GPtrArray *args;
54         GPid pid;
55         gboolean done;
56
57         debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n",
58                       n, path, offer_basic ? "" : "don't ",
59                       offer_digest ? "" : "don't ",
60                       good_user ? "good" : "bad",
61                       good_password ? "good" : "bad");
62
63         uri = soup_uri_new_with_base (base_uri, path);
64         uri_str = soup_uri_to_string (uri, FALSE);
65         soup_uri_free (uri);
66
67         args = g_ptr_array_new ();
68         g_ptr_array_add (args, "curl");
69         g_ptr_array_add (args, "-f");
70         g_ptr_array_add (args, "-s");
71         if (offer_basic || offer_digest) {
72                 g_ptr_array_add (args, "-u");
73                 if (good_user) {
74                         if (good_password)
75                                 g_ptr_array_add (args, "user:password");
76                         else
77                                 g_ptr_array_add (args, "user:badpassword");
78                 } else {
79                         if (good_password)
80                                 g_ptr_array_add (args, "baduser:password");
81                         else
82                                 g_ptr_array_add (args, "baduser:badpassword");
83                 }
84
85                 if (offer_basic && offer_digest)
86                         g_ptr_array_add (args, "--anyauth");
87                 else if (offer_basic)
88                         g_ptr_array_add (args, "--basic");
89                 else
90                         g_ptr_array_add (args, "--digest");
91         }
92         g_ptr_array_add (args, uri_str);
93         g_ptr_array_add (args, NULL);
94
95         memset (&test_data, 0, sizeof (test_data));
96         if (g_spawn_async (NULL, (char **)args->pdata, NULL,
97                            G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
98                            NULL, NULL, &pid, NULL)) {
99                 done = FALSE;
100                 g_child_watch_add (pid, curl_exited, &done);
101
102                 while (!done)
103                         g_main_context_iteration (NULL, TRUE);
104         } else
105                 test_data.succeeded = FALSE;
106         g_ptr_array_free (args, TRUE);
107         g_free (uri_str);
108
109         if (server_requests_basic != test_data.server_requested_basic) {
110                 errors++;
111                 if (test_data.server_requested_basic)
112                         debug_printf (1, "  Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
113                 else
114                         debug_printf (1, "  Server didn't send WWW-Authenticate: Basic, but should have!\n");
115         }
116         if (server_requests_digest != test_data.server_requested_digest) {
117                 errors++;
118                 if (test_data.server_requested_digest)
119                         debug_printf (1, "  Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
120                 else
121                         debug_printf (1, "  Server didn't send WWW-Authenticate: Digest, but should have!\n");
122         }
123         if (client_sends_basic != test_data.client_sent_basic) {
124                 errors++;
125                 if (test_data.client_sent_basic)
126                         debug_printf (1, "  Client sent Authorization: Basic, but shouldn't have!\n");
127                 else
128                         debug_printf (1, "  Client didn't send Authorization: Basic, but should have!\n");
129         }
130         if (client_sends_digest != test_data.client_sent_digest) {
131                 errors++;
132                 if (test_data.client_sent_digest)
133                         debug_printf (1, "  Client sent Authorization: Digest, but shouldn't have!\n");
134                 else
135                         debug_printf (1, "  Client didn't send Authorization: Digest, but should have!\n");
136         }
137         if (success && !test_data.succeeded) {
138                 errors++;
139                 debug_printf (1, "  Should have succeeded, but didn't!\n");
140         } else if (!success && test_data.succeeded) {
141                 errors++;
142                 debug_printf (1, "  Should not have succeeded, but did!\n");
143         }
144 }
145
146 static void
147 do_auth_tests (SoupURI *base_uri)
148 {
149         int i, n = 1;
150         gboolean use_basic, use_digest, good_user, good_password;
151         gboolean preemptive_basic, good_auth;
152
153         for (i = 0; i < 16; i++) {
154                 use_basic     = (i & 1) == 1;
155                 use_digest    = (i & 2) == 2;
156                 good_user     = (i & 4) == 4;
157                 good_password = (i & 8) == 8;
158
159                 good_auth = good_user && good_password;
160
161                 /* Curl will preemptively send Basic if it's told to
162                  * use Basic but not Digest.
163                  */
164                 preemptive_basic = use_basic && !use_digest;
165
166                 /* 1. No auth required. The server will ignore the
167                  * Authorization headers completely, and the request
168                  * will always succeed.
169                  */
170                 do_test (n++, base_uri, "/foo",
171                          good_user, good_password,
172                          /* request */
173                          use_basic, use_digest,
174                          /* expected from client */
175                          preemptive_basic, FALSE,
176                          /* expected from server */
177                          FALSE, FALSE,
178                          /* success? */
179                          TRUE);
180
181                 /* 2. Basic auth required. The server will send
182                  * "WWW-Authenticate: Basic" if the client fails to
183                  * send an Authorization: Basic on the first request,
184                  * or if it sends a bad password.
185                  */
186                 do_test (n++, base_uri, "/Basic/foo",
187                          good_user, good_password,
188                          /* request */
189                          use_basic, use_digest,
190                          /* expected from client */
191                          use_basic, FALSE,
192                          /* expected from server */
193                          !preemptive_basic || !good_auth, FALSE,
194                          /* success? */
195                          use_basic && good_auth);
196
197                 /* 3. Digest auth required. Simpler than the basic
198                  * case because the client can't send Digest auth
199                  * premptively.
200                  */
201                 do_test (n++, base_uri, "/Digest/foo",
202                          good_user, good_password,
203                          /* request */
204                          use_basic, use_digest,
205                          /* expected from client */
206                          preemptive_basic, use_digest,
207                          /* expected from server */
208                          FALSE, TRUE,
209                          /* success? */
210                          use_digest && good_auth);
211
212                 /* 4. Any auth required. */
213                 do_test (n++, base_uri, "/Any/foo",
214                          good_user, good_password,
215                          /* request */
216                          use_basic, use_digest,
217                          /* expected from client */
218                          preemptive_basic, use_digest,
219                          /* expected from server */
220                          !preemptive_basic || !good_auth, !preemptive_basic || !good_auth,
221                          /* success? */
222                          (use_basic || use_digest) && good_auth);
223
224                 /* 5. No auth required again. (Makes sure that
225                  * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
226                  */
227                 do_test (n++, base_uri, "/Any/Not/foo",
228                          good_user, good_password,
229                          /* request */
230                          use_basic, use_digest,
231                          /* expected from client */
232                          preemptive_basic, FALSE,
233                          /* expected from server */
234                          FALSE, FALSE,
235                          /* success? */
236                          TRUE);
237         }
238 }
239
240 static gboolean
241 basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
242                      const char *username, const char *password, gpointer data)
243 {
244         return !strcmp (username, "user") && !strcmp (password, "password");
245 }
246
247 static char *
248 digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
249                       const char *username, gpointer data)
250 {
251         if (strcmp (username, "user") != 0)
252                 return NULL;
253
254         /* Note: this is exactly how you *shouldn't* do it in the real
255          * world; you should have the pre-encoded password stored in a
256          * database of some sort rather than using the cleartext
257          * password in the callback.
258          */
259         return soup_auth_domain_digest_encode_password ("user",
260                                                         "server-auth-test",
261                                                         "password");
262 }
263
264 static void
265 server_callback (SoupServer *server, SoupMessage *msg,
266                  const char *path, GHashTable *query,
267                  SoupClientContext *context, gpointer data)
268 {
269         if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
270                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
271                 return;
272         }
273
274         soup_message_set_response (msg, "text/plain",
275                                    SOUP_MEMORY_STATIC,
276                                    "OK\r\n", 4);
277         soup_message_set_status (msg, SOUP_STATUS_OK);
278 }
279
280 static void
281 got_headers_callback (SoupMessage *msg, gpointer data)
282 {
283         const char *header;
284
285         header = soup_message_headers_get_one (msg->request_headers,
286                                                "Authorization");
287         if (header) {
288                 if (strstr (header, "Basic "))
289                         test_data.client_sent_basic = TRUE;
290                 if (strstr (header, "Digest "))
291                         test_data.client_sent_digest = TRUE;
292         }
293 }
294
295 static void
296 wrote_headers_callback (SoupMessage *msg, gpointer data)
297 {
298         const char *header;
299
300         header = soup_message_headers_get_list (msg->response_headers,
301                                                 "WWW-Authenticate");
302         if (header) {
303                 if (strstr (header, "Basic "))
304                         test_data.server_requested_basic = TRUE;
305                 if (strstr (header, "Digest "))
306                         test_data.server_requested_digest = TRUE;
307         }
308 }
309
310 static void
311 request_started_callback (SoupServer *server, SoupMessage *msg,
312                           SoupClientContext *client, gpointer data)
313 {
314         g_signal_connect (msg, "got_headers",
315                           G_CALLBACK (got_headers_callback), NULL);
316         g_signal_connect (msg, "wrote_headers",
317                           G_CALLBACK (wrote_headers_callback), NULL);
318 }
319
320 static gboolean run_tests = TRUE;
321
322 static GOptionEntry no_test_entry[] = {
323         { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
324           G_OPTION_ARG_NONE, &run_tests,
325           "Don't run tests, just run the test server", NULL },
326         { NULL }
327 };
328
329 int
330 main (int argc, char **argv)
331 {
332         GMainLoop *loop;
333         SoupServer *server;
334         SoupURI *uri;
335         SoupAuthDomain *auth_domain;
336
337         test_init (argc, argv, no_test_entry);
338
339         server = soup_test_server_new (FALSE);
340         g_signal_connect (server, "request_started",
341                           G_CALLBACK (request_started_callback), NULL);
342         soup_server_add_handler (server, NULL,
343                                  server_callback, NULL, NULL);
344
345         auth_domain = soup_auth_domain_basic_new (
346                 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
347                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
348                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
349                 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
350                 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
351                 NULL);
352         soup_server_add_auth_domain (server, auth_domain);
353         g_object_unref (auth_domain);
354
355         auth_domain = soup_auth_domain_digest_new (
356                 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
357                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
358                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
359                 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
360                 SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
361                 NULL);
362         soup_server_add_auth_domain (server, auth_domain);
363         g_object_unref (auth_domain);
364
365         loop = g_main_loop_new (NULL, TRUE);
366
367         if (run_tests) {
368                 uri = soup_uri_new ("http://127.0.0.1");
369                 soup_uri_set_port (uri, soup_server_get_port (server));
370                 do_auth_tests (uri);
371                 soup_uri_free (uri);
372         } else {
373                 printf ("Listening on port %d\n", soup_server_get_port (server));
374                 g_main_loop_run (loop);
375         }
376
377         g_main_loop_unref (loop);
378         soup_test_server_quit_unref (server);
379
380         if (run_tests)
381                 test_cleanup ();
382         return errors != 0;
383 }