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