url.c (5003B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include "url.h" 6 7 struct encode_test_case { 8 const char *name; 9 const char *input; 10 const char *expected; 11 }; 12 13 struct decode_test_case { 14 const char *name; 15 const char *input; 16 const char *expected; 17 int expected_result; 18 }; 19 20 static const struct encode_test_case encode_tests[] = { 21 /* Unreserved characters should not be encoded. */ 22 { "simple filename", "file.txt", "file.txt" }, 23 { "alphanumeric", "file123", "file123" }, 24 { "hyphen", "my-file.txt", "my-file.txt" }, 25 { "underscore", "my_file.txt", "my_file.txt" }, 26 { "dot", "file.txt", "file.txt" }, 27 { "tilde", "~file.txt", "~file.txt" }, 28 29 /* Characters requiring encoding. */ 30 { "space", "my file.txt", "my%20file.txt" }, 31 { "ampersand", "&file.txt", "%26file.txt" }, 32 { "plus", "a+b.txt", "a%2Bb.txt" }, 33 { "percent", "100%.txt", "100%25.txt" }, 34 { "question mark", "file?.txt", "file%3F.txt" }, 35 { "equals", "file=name.txt", "file%3Dname.txt" }, 36 { "slash", "foo/bar.txt", "foo%2Fbar.txt" }, 37 { "backslash", "foo\\bar.txt", "foo%5Cbar.txt" }, 38 { "hash", "file#1.txt", "file%231.txt" }, 39 { "quote", "file\".txt", "file%22.txt" }, 40 41 /* Empty string. */ 42 { "empty", "", "" }, 43 }; 44 45 static const struct decode_test_case decode_tests[] = { 46 /* Plain strings. */ 47 { "simple filename", "file.txt", "file.txt", 0 }, 48 { "alphanumeric", "file123", "file123", 0 }, 49 { "hyphen", "my-file.txt", "my-file.txt", 0 }, 50 { "underscore", "my_file.txt", "my_file.txt", 0 }, 51 { "dot", "file.txt", "file.txt", 0 }, 52 { "tilde", "~file.txt", "~file.txt", 0 }, 53 54 /* Encoded characters. */ 55 { "space", "my%20file.txt", "my file.txt", 0 }, 56 { "plus", "my+file.txt", "my file.txt", 0 }, 57 { "ampersand", "%26file.txt", "&file.txt", 0 }, 58 { "percent", "100%25.txt", "100%.txt", 0 }, 59 { "question mark", "file%3F.txt", "file?.txt", 0 }, 60 { "equals", "file%3Dname.txt", "file=name.txt", 0 }, 61 { "slash", "foo%2Fbar.txt", "foo/bar.txt", 0 }, 62 { "backslash", "foo%5Cbar.txt", "foo\\bar.txt", 0 }, 63 { "hash", "file%23.txt", "file#.txt", 0 }, 64 { "quote", "file%22.txt", "file\".txt", 0 }, 65 66 /* Hexadecimal case should both work. */ 67 { "lowercase hex", "%2f", "/", 0 }, 68 { "uppercase hex", "%2F", "/", 0 }, 69 { "mixed hex", "%2f%2F", "//", 0 }, 70 71 /* Encoded traversal must decode successfully; 72 * valid_filename() is responsible for rejecting it. */ 73 { "encoded dot", "%2e", ".", 0 }, 74 { "encoded dot dot", "%2e%2e", "..", 0 }, 75 { "encoded traversal", "%2e%2e%2ffile.txt", 76 "../file.txt", 0 }, 77 78 /* Empty string. */ 79 { "empty", "", "", 0 }, 80 81 /* Invalid percent encodings. */ 82 { "trailing percent", "%", NULL, -1 }, 83 { "one hex digit", "%2", NULL, -1 }, 84 { "invalid hex", "%ZZ", NULL, -1 }, 85 { "invalid high digit", "%G0", NULL, -1 }, 86 { "invalid low digit", "%0G", NULL, -1 }, 87 { "incomplete encoding", "file%2", NULL, -1 }, 88 89 /* Encoded NUL must be rejected. */ 90 { "encoded NUL", "file%00.txt", NULL, -1 }, 91 }; 92 93 static int 94 test_encode(void) 95 { 96 size_t i; 97 int failures = 0; 98 99 for (i = 0; 100 i < sizeof(encode_tests) / sizeof(encode_tests[0]); 101 i++) { 102 char *got; 103 104 got = url_encode(encode_tests[i].input); 105 106 if (got == NULL) { 107 fprintf(stderr, 108 "FAIL: %s: url_encode() returned NULL\n", 109 encode_tests[i].name); 110 failures++; 111 continue; 112 } 113 114 if (strcmp(got, encode_tests[i].expected) != 0) { 115 fprintf(stderr, 116 "FAIL: %s: url_encode(%s) returned %s, " 117 "expected %s\n", 118 encode_tests[i].name, 119 encode_tests[i].input, 120 got, 121 encode_tests[i].expected); 122 failures++; 123 } else { 124 printf("ok: encode %s\n", 125 encode_tests[i].name); 126 } 127 128 free(got); 129 } 130 131 return failures; 132 } 133 134 static int 135 test_decode(void) 136 { 137 size_t i; 138 int failures = 0; 139 140 for (i = 0; 141 i < sizeof(decode_tests) / sizeof(decode_tests[0]); 142 i++) { 143 char *input; 144 int got; 145 146 input = malloc(strlen(decode_tests[i].input) + 1); 147 if (input == NULL) { 148 fprintf(stderr, 149 "FAIL: %s: malloc failed\n", 150 decode_tests[i].name); 151 failures++; 152 continue; 153 } 154 155 strcpy(input, decode_tests[i].input); 156 157 got = url_decode(input); 158 159 if (got != decode_tests[i].expected_result) { 160 fprintf(stderr, 161 "FAIL: %s: url_decode() returned %d, " 162 "expected %d\n", 163 decode_tests[i].name, 164 got, 165 decode_tests[i].expected_result); 166 failures++; 167 free(input); 168 continue; 169 } 170 171 if (got == 0 && 172 strcmp(input, decode_tests[i].expected) != 0) { 173 fprintf(stderr, 174 "FAIL: %s: url_decode(%s) returned %s, " 175 "expected %s\n", 176 decode_tests[i].name, 177 decode_tests[i].input, 178 input, 179 decode_tests[i].expected); 180 failures++; 181 } else { 182 printf("ok: decode %s\n", 183 decode_tests[i].name); 184 } 185 186 free(input); 187 } 188 189 return failures; 190 } 191 192 int 193 main(void) 194 { 195 int failures; 196 197 failures = 0; 198 199 failures += test_encode(); 200 failures += test_decode(); 201 202 if (failures != 0) { 203 fprintf(stderr, 204 "%d test(s) failed\n", 205 failures); 206 return EXIT_FAILURE; 207 } 208 209 printf("all tests passed\n"); 210 return EXIT_SUCCESS; 211 } 212