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