add missing slash in %configure
[platform/upstream/libsoup.git] / tests / forms-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007, 2008 Red Hat, Inc.
4  */
5
6 #include "test-utils.h"
7
8 static struct {
9         const char *title, *name;
10         const char *result;
11 } tests[] = {
12         /* Both fields must be filled in */
13         { NULL, "Name", "" },
14         { "Mr.", NULL, "" },
15
16         /* Filled-in but empty is OK */
17         { "", "", "Hello,  " },
18         { "", "Name", "Hello,  Name" },
19         { "Mr.", "", "Hello, MR. " },
20
21         /* Simple */
22         { "Mr.", "Name", "Hello, MR. Name" },
23
24         /* Encoding of spaces */
25         { "Mr.", "Full Name", "Hello, MR. Full Name" },
26         { "Mr. and Mrs.", "Full Name", "Hello, MR. AND MRS. Full Name" },
27
28         /* Encoding of "+" */
29         { "Mr.+Mrs.", "Full Name", "Hello, MR.+MRS. Full Name" },
30
31         /* Encoding of non-ASCII. */
32         { "Se\xC3\xB1or", "Nombre", "Hello, SE\xC3\xB1OR Nombre" },
33
34         /* Encoding of '%' */
35         { "Mr.", "Foo %2f Bar", "Hello, MR. Foo %2f Bar" },
36 };
37
38 static void
39 do_hello_test (int n, gboolean extra, const char *uri)
40 {
41         GPtrArray *args;
42         char *title_arg = NULL, *name_arg = NULL;
43         char *str_stdout = NULL;
44         GError *error = NULL;
45
46         debug_printf (1, "%2d. '%s' '%s'%s: ", n * 2 + (extra ? 2 : 1),
47                       tests[n].title ? tests[n].title : "(null)",
48                       tests[n].name  ? tests[n].name  : "(null)",
49                       extra ? " + extra" : "");
50
51         args = g_ptr_array_new ();
52         g_ptr_array_add (args, "curl");
53         g_ptr_array_add (args, "--noproxy");
54         g_ptr_array_add (args, "*");
55         g_ptr_array_add (args, "-G");
56         if (tests[n].title) {
57                 title_arg = soup_form_encode ("title", tests[n].title, NULL);
58                 g_ptr_array_add (args, "-d");
59                 g_ptr_array_add (args, title_arg);
60         }
61         if (tests[n].name) {
62                 name_arg = soup_form_encode ("n@me", tests[n].name, NULL);
63                 g_ptr_array_add (args, "-d");
64                 g_ptr_array_add (args, name_arg);
65         }
66         if (extra) {
67                 g_ptr_array_add (args, "-d");
68                 g_ptr_array_add (args, "extra=something");
69         }
70         g_ptr_array_add (args, (char *)uri);
71         g_ptr_array_add (args, NULL);
72
73         if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
74                           G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
75                           NULL, NULL,
76                           &str_stdout, NULL, NULL, &error)) {
77                 g_assert_cmpstr (str_stdout, ==, tests[n].result);
78                 g_free (str_stdout);
79         } else {
80                 g_assert_no_error (error);
81                 g_error_free (error);
82         }
83         g_ptr_array_free (args, TRUE);
84         g_free (title_arg);
85         g_free (name_arg);
86 }
87
88 static void
89 do_hello_tests (gconstpointer uri)
90 {
91         int n;
92
93 #ifndef HAVE_CURL
94         g_test_skip ("/usr/bin/curl is not available");
95         return;
96 #endif
97
98         for (n = 0; n < G_N_ELEMENTS (tests); n++) {
99                 do_hello_test (n, FALSE, uri);
100                 do_hello_test (n, TRUE, uri);
101         }
102 }
103
104 #define MD5_TEST_FILE (g_test_get_filename (G_TEST_DIST, "index.txt", NULL))
105 #define MD5_TEST_FILE_BASENAME "index.txt"
106 #define MD5_TEST_FILE_MIME_TYPE "text/plain"
107
108 static char *
109 get_md5_data (char **contents, gsize *length)
110 {
111         char *my_contents, *md5;
112         gsize my_length;
113         GError *error = NULL;
114
115         if (!g_file_get_contents (MD5_TEST_FILE, &my_contents, &my_length, &error)) {
116                 g_assert_no_error (error);
117                 g_error_free (error);
118                 return NULL;
119         }
120
121         md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, my_contents, my_length);
122
123         if (contents)
124                 *contents = my_contents;
125         else
126                 g_free (my_contents);
127         if (length)
128                 *length = my_length;
129
130         return md5;
131 }
132
133 static void
134 do_md5_test_curl (gconstpointer data)
135 {
136         const char *uri = data;
137         char *md5;
138         GPtrArray *args;
139         char *file_arg, *str_stdout;
140         GError *error = NULL;
141
142 #ifndef HAVE_CURL
143         g_test_skip ("/usr/bin/curl is not available");
144         return;
145 #endif
146
147         md5 = get_md5_data (NULL, NULL);
148         if (!md5)
149                 return;
150
151         args = g_ptr_array_new ();
152         g_ptr_array_add (args, "curl");
153         g_ptr_array_add (args, "--noproxy");
154         g_ptr_array_add (args, "*");
155         g_ptr_array_add (args, "-L");
156         g_ptr_array_add (args, "-F");
157         file_arg = g_strdup_printf ("file=@%s", MD5_TEST_FILE);
158         g_ptr_array_add (args, file_arg);
159         g_ptr_array_add (args, "-F");
160         g_ptr_array_add (args, "fmt=txt");
161         g_ptr_array_add (args, (char *)uri);
162         g_ptr_array_add (args, NULL);
163
164         if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
165                           G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
166                           NULL, NULL,
167                           &str_stdout, NULL, NULL, NULL)) {
168                 g_assert_cmpstr (str_stdout, ==, md5);
169                 g_free (str_stdout);
170         } else {
171                 g_assert_no_error (error);
172                 g_error_free (error);
173         }
174         g_ptr_array_free (args, TRUE);
175         g_free (file_arg);
176
177         g_free (md5);
178 }
179
180 static void
181 do_md5_test_libsoup (gconstpointer data)
182 {
183         const char *uri = data;
184         char *contents, *md5;
185         gsize length;
186         SoupMultipart *multipart;
187         SoupBuffer *buffer;
188         SoupMessage *msg;
189         SoupSession *session;
190
191         g_test_bug ("601640");
192
193         md5 = get_md5_data (&contents, &length);
194         if (!md5)
195                 return;
196
197         multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
198         buffer = soup_buffer_new (SOUP_MEMORY_COPY, contents, length);
199         soup_multipart_append_form_file (multipart, "file",
200                                          MD5_TEST_FILE_BASENAME,
201                                          MD5_TEST_FILE_MIME_TYPE,
202                                          buffer);
203         soup_buffer_free (buffer);
204         soup_multipart_append_form_string (multipart, "fmt", "text");
205
206         msg = soup_form_request_new_from_multipart (uri, multipart);
207         soup_multipart_free (multipart);
208
209         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
210         soup_session_send_message (session, msg);
211
212         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
213         g_assert_cmpstr (msg->response_body->data, ==, md5);
214
215         g_object_unref (msg);
216         soup_test_session_abort_unref (session);
217
218         g_free (contents);
219         g_free (md5);
220 }
221
222 static void
223 do_form_decode_test (void)
224 {
225         GHashTable *table;
226         const gchar *value;
227         gchar *tmp;
228
229 #ifndef HAVE_CURL
230         g_test_skip ("/usr/bin/curl is not available");
231         return;
232 #endif
233
234         /*  Test that the code handles multiple values with the same key.  */
235         table = soup_form_decode ("foo=first&foo=second&foo=third");
236
237         /*  Allocate some memory. We do this to test for a bug in
238          *  soup_form_decode() that resulted in values from the hash
239          *  table pointing to memory that is already released.
240          */
241         tmp = g_strdup ("other");
242
243         value = g_hash_table_lookup (table, "foo");
244         g_assert_cmpstr (value, ==, "third");
245
246         g_free (tmp);
247         g_hash_table_destroy (table);
248 }
249
250 static void
251 hello_callback (SoupServer *server, SoupMessage *msg,
252                 const char *path, GHashTable *query,
253                 SoupClientContext *context, gpointer data)
254 {
255         char *title, *name, *fmt;
256         const char *content_type;
257         GString *buf;
258
259         if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
260                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
261                 return;
262         }
263
264         if (query) {
265                 title = g_hash_table_lookup (query, "title");
266                 name = g_hash_table_lookup (query, "n@me");
267                 fmt = g_hash_table_lookup (query, "fmt");
268         } else
269                 title = name = fmt = NULL;
270
271         buf = g_string_new (NULL);
272         if (!query || (fmt && !strcmp (fmt, "html"))) {
273                 content_type = "text/html";
274                 g_string_append (buf, "<html><head><title>forms-test: hello</title></head><body>\r\n");
275                 if (title && name) {
276                         /* mumble mumble html-escape... */
277                         g_string_append_printf (buf, "<p>Hello, <b><em>%s</em> %s</b></p>\r\n",
278                                                 title, name);
279                 }
280                 g_string_append (buf, "<form action='/hello' method='get'>"
281                                  "<p>Title: <input name='title'></p>"
282                                  "<p>Name: <input name='n@me'></p>"
283                                  "<p><input type=hidden name='fmt' value='html'></p>"
284                                  "<p><input type=submit></p>"
285                                  "</form>\r\n");
286                 g_string_append (buf, "</body></html>\r\n");
287         } else {
288                 content_type = "text/plain";
289                 if (title && name) {
290                         char *uptitle = g_ascii_strup (title, -1);
291                         g_string_append_printf (buf, "Hello, %s %s",
292                                                 uptitle, name);
293                         g_free (uptitle);
294                 }
295         }
296
297         soup_message_set_response (msg, content_type,
298                                    SOUP_MEMORY_TAKE,
299                                    buf->str, buf->len);
300         g_string_free (buf, FALSE);
301         soup_message_set_status (msg, SOUP_STATUS_OK);
302 }
303
304 static void
305 md5_get_callback (SoupServer *server, SoupMessage *msg,
306                   const char *path, GHashTable *query,
307                   SoupClientContext *context, gpointer data)
308 {
309         const char *file = NULL, *md5sum = NULL, *fmt;
310         const char *content_type;
311         GString *buf;
312
313         if (query) {
314                 file = g_hash_table_lookup (query, "file");
315                 md5sum = g_hash_table_lookup (query, "md5sum");
316                 fmt = g_hash_table_lookup (query, "fmt");
317         } else
318                 fmt = "html";
319
320         buf = g_string_new (NULL);
321         if (!strcmp (fmt, "html")) {
322                 content_type = "text/html";
323                 g_string_append (buf, "<html><head><title>forms-test: md5</title></head><body>\r\n");
324                 if (file && md5sum) {
325                         /* mumble mumble html-escape... */
326                         g_string_append_printf (buf, "<p>File: %s<br>MD5: <b>%s</b></p>\r\n",
327                                                 file, md5sum);
328                 }
329                 g_string_append (buf, "<form action='/md5' method='post' enctype='multipart/form-data'>"
330                                  "<p>File: <input type='file' name='file'></p>"
331                                  "<p><input type=hidden name='fmt' value='html'></p>"
332                                  "<p><input type=submit></p>"
333                                  "</form>\r\n");
334                 g_string_append (buf, "</body></html>\r\n");
335         } else {
336                 content_type = "text/plain";
337                 if (md5sum)
338                         g_string_append_printf (buf, "%s", md5sum);
339         }
340
341         soup_message_set_response (msg, content_type,
342                                    SOUP_MEMORY_TAKE,
343                                    buf->str, buf->len);
344         g_string_free (buf, FALSE);
345         soup_message_set_status (msg, SOUP_STATUS_OK);
346 }
347
348 static void
349 md5_post_callback (SoupServer *server, SoupMessage *msg,
350                    const char *path, GHashTable *query,
351                    SoupClientContext *context, gpointer data)
352 {
353         const char *content_type;
354         GHashTable *params;
355         const char *fmt;
356         char *filename, *md5sum, *redirect_uri;
357         SoupBuffer *file;
358         SoupURI *uri;
359
360         content_type = soup_message_headers_get_content_type (msg->request_headers, NULL);
361         if (!content_type || strcmp (content_type, "multipart/form-data") != 0) {
362                 soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
363                 return;
364         }
365
366         params = soup_form_decode_multipart (msg, "file",
367                                              &filename, NULL, &file);
368         if (!params) {
369                 soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
370                 return;
371         }
372         fmt = g_hash_table_lookup (params, "fmt");
373
374         md5sum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
375                                               (gpointer)file->data,
376                                               file->length);
377         soup_buffer_free (file);
378
379         uri = soup_uri_copy (soup_message_get_uri (msg));
380         soup_uri_set_query_from_fields (uri,
381                                         "file", filename ? filename : "",
382                                         "md5sum", md5sum,
383                                         "fmt", fmt ? fmt : "html",
384                                         NULL);
385         redirect_uri = soup_uri_to_string (uri, FALSE);
386
387         soup_message_set_redirect (msg, SOUP_STATUS_SEE_OTHER, redirect_uri);
388
389         g_free (redirect_uri);
390         soup_uri_free (uri);
391         g_free (md5sum);
392         g_free (filename);
393         g_hash_table_destroy (params);
394 }
395
396 static void
397 md5_callback (SoupServer *server, SoupMessage *msg,
398               const char *path, GHashTable *query,
399               SoupClientContext *context, gpointer data)
400 {
401         if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
402                 md5_get_callback (server, msg, path, query, context, data);
403         else if (msg->method == SOUP_METHOD_POST)
404                 md5_post_callback (server, msg, path, query, context, data);
405         else
406                 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
407 }
408
409 static gboolean run_tests = TRUE;
410
411 static GOptionEntry no_test_entry[] = {
412         { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
413           G_OPTION_ARG_NONE, &run_tests,
414           "Don't run tests, just run the test server", NULL },
415         { NULL }
416 };
417
418 int
419 main (int argc, char **argv)
420 {
421         GMainLoop *loop;
422         SoupServer *server;
423         guint port;
424         char *uri_str;
425         int ret = 0;
426
427         test_init (argc, argv, no_test_entry);
428
429         server = soup_test_server_new (TRUE);
430         soup_server_add_handler (server, "/hello",
431                                  hello_callback, NULL, NULL);
432         soup_server_add_handler (server, "/md5",
433                                  md5_callback, NULL, NULL);
434         port = soup_server_get_port (server);
435
436         loop = g_main_loop_new (NULL, TRUE);
437
438         if (run_tests) {
439                 uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port);
440                 g_test_add_data_func_full ("/forms/hello", uri_str, do_hello_tests, g_free);
441
442                 uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port);
443                 g_test_add_data_func_full ("/forms/md5/curl", g_strdup (uri_str), do_md5_test_curl, g_free);
444                 g_test_add_data_func_full ("/forms/md5/libsoup", g_strdup (uri_str), do_md5_test_libsoup, g_free);
445                 g_free (uri_str);
446
447                 g_test_add_func ("/forms/decode", do_form_decode_test);
448
449                 ret = g_test_run ();
450         } else {
451                 g_print ("Listening on port %d\n", port);
452                 g_main_loop_run (loop);
453         }
454
455         g_main_loop_unref (loop);
456
457         soup_test_server_quit_unref (server);
458         if (run_tests)
459                 test_cleanup ();
460         return ret;
461 }