libtest_convenience_server_reply.c (28015B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2024 Christian Grothoff 5 Copyright (C) 2024 Evgeny Grin (Karlson2k) 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file libtest_convenience_server_reply.c 42 * @brief convenience functions that generate 43 * replies from the server for libtest users 44 * @author Christian Grothoff 45 */ 46 #include "libtest.h" 47 #include <pthread.h> 48 #include <stdbool.h> 49 #include <fcntl.h> 50 #include <stdio.h> 51 #include <unistd.h> 52 #include <errno.h> 53 #include <curl/curl.h> 54 #include <assert.h> 55 #include "mhdt_tmpfile.h" 56 57 #ifndef CURL_VERSION_BITS 58 # define CURL_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z)) 59 #endif 60 #ifndef CURL_AT_LEAST_VERSION 61 # define CURL_AT_LEAST_VERSION(x, y, z) \ 62 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) 63 #endif 64 65 const struct MHD_Action * 66 MHDT_server_reply_text ( 67 void *cls, 68 struct MHD_Request *MHD_RESTRICT request, 69 const struct MHD_String *MHD_RESTRICT path, 70 enum MHD_HTTP_Method method, 71 uint_fast64_t upload_size) 72 { 73 const char *text = cls; 74 75 (void)path; 76 (void)method; 77 (void)upload_size; /* Unused */ 78 79 return MHD_action_from_response ( 80 request, 81 MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK, 82 strlen (text), 83 text)); 84 } 85 86 87 /** 88 * Create a temporary file and put the @a text to it. 89 * Exit the test if the file cannot be created or written. 90 * 91 * @param text the text to put to the file 92 * @return the FD of the file with the @a text 93 */ 94 static int 95 tmpfile_with_text (const char *text) 96 { 97 size_t written = 0u; 98 size_t tlen = strlen (text); 99 int fd; 100 101 fd = MHDT_create_tmpfile (); 102 if (-1 == fd) 103 { 104 fprintf (stderr, 105 "Failed to create a temporary file\n"); 106 fflush (stderr); 107 exit (99); 108 } 109 110 while (written < tlen) 111 { 112 ssize_t wr_chunk = write (fd, 113 text + written, 114 tlen - written); 115 if (0 == wr_chunk) 116 break; 117 else if (0 > wr_chunk) 118 { 119 if (EINTR == errno) 120 continue; 121 break; 122 } 123 written += (size_t)wr_chunk; 124 } 125 if (written < tlen) 126 { 127 fprintf (stderr, 128 "Failed to write() a temporary file: %s\n", 129 strerror (errno)); 130 (void)close (fd); 131 fflush (stderr); 132 exit (99); 133 } 134 return fd; 135 } 136 137 138 const struct MHD_Action * 139 MHDT_server_reply_file ( 140 void *cls, 141 struct MHD_Request *MHD_RESTRICT request, 142 const struct MHD_String *MHD_RESTRICT path, 143 enum MHD_HTTP_Method method, 144 uint_fast64_t upload_size) 145 { 146 const char *text = cls; 147 148 (void)path; 149 (void)method; 150 (void)upload_size; /* Unused */ 151 152 return MHD_action_from_response ( 153 request, 154 MHD_response_from_fd (MHD_HTTP_STATUS_OK, 155 tmpfile_with_text (text), 156 0 /* offset */, 157 strlen (text))); 158 } 159 160 161 const struct MHD_Action * 162 MHDT_server_reply_file_unknown_size ( 163 void *cls, 164 struct MHD_Request *MHD_RESTRICT request, 165 const struct MHD_String *MHD_RESTRICT path, 166 enum MHD_HTTP_Method method, 167 uint_fast64_t upload_size) 168 { 169 const char *text = cls; 170 171 (void)path; 172 (void)method; 173 (void)upload_size; /* Unused */ 174 175 return MHD_action_from_response ( 176 request, 177 MHD_response_from_fd (MHD_HTTP_STATUS_OK, 178 tmpfile_with_text (text), 179 0 /* offset */, 180 MHD_SIZE_UNKNOWN)); 181 } 182 183 184 const struct MHD_Action * 185 MHDT_server_reply_with_header ( 186 void *cls, 187 struct MHD_Request *MHD_RESTRICT request, 188 const struct MHD_String *MHD_RESTRICT path, 189 enum MHD_HTTP_Method method, 190 uint_fast64_t upload_size) 191 { 192 const char *header = cls; 193 size_t hlen = strlen (header) + 1; 194 char name[hlen]; 195 const char *colon = strchr (header, ':'); 196 const char *value; 197 struct MHD_Response *resp; 198 199 (void)path; 200 (void)method; 201 (void)upload_size; /* Unused */ 202 203 memcpy (name, 204 header, 205 hlen); 206 name[colon - header] = '\0'; 207 value = &name[colon - header + 1]; 208 209 resp = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); 210 if (MHD_SC_OK != 211 MHD_response_add_header (resp, 212 name, 213 value)) 214 { 215 MHD_response_destroy (resp); 216 return MHD_action_abort_request (request); 217 } 218 return MHD_action_from_response ( 219 request, 220 resp); 221 } 222 223 224 const struct MHD_Action * 225 MHDT_server_reply_check_query ( 226 void *cls, 227 struct MHD_Request *MHD_RESTRICT request, 228 const struct MHD_String *MHD_RESTRICT path, 229 enum MHD_HTTP_Method method, 230 uint_fast64_t upload_size) 231 { 232 const char *equery = cls; 233 size_t qlen = strlen (equery) + 1; 234 char qc[qlen]; 235 236 (void)path; 237 (void)method; 238 (void)upload_size; /* Unused */ 239 240 memcpy (qc, 241 equery, 242 qlen); 243 for (const char *tok = strtok (qc, "&"); 244 NULL != tok; 245 tok = strtok (NULL, "&")) 246 { 247 const char *end; 248 struct MHD_StringNullable sn; 249 const char *val; 250 251 end = strchr (tok, '='); 252 if (NULL == end) 253 { 254 end = &tok[strlen (tok)]; 255 val = NULL; 256 } 257 else 258 { 259 val = end + 1; 260 } 261 { 262 size_t alen = (size_t)(end - tok); 263 char arg[alen + 1]; 264 265 memcpy (arg, 266 tok, 267 alen); 268 arg[alen] = '\0'; 269 if (MHD_NO == 270 MHD_request_get_value (request, 271 MHD_VK_URI_QUERY_PARAM, 272 arg, 273 &sn)) 274 { 275 fprintf (stderr, 276 "NULL returned for query key %s\n", 277 arg); 278 return MHD_action_abort_request (request); 279 } 280 if (NULL == val) 281 { 282 if (NULL != sn.cstr) 283 { 284 fprintf (stderr, 285 "NULL expected for value for query key %s, got %s\n", 286 arg, 287 sn.cstr); 288 return MHD_action_abort_request (request); 289 } 290 } 291 else 292 { 293 if (NULL == sn.cstr) 294 { 295 fprintf (stderr, 296 "%s expected for value for query key %s, got NULL\n", 297 val, 298 arg); 299 return MHD_action_abort_request (request); 300 } 301 if (0 != strcmp (val, 302 sn.cstr)) 303 { 304 fprintf (stderr, 305 "%s expected for value for query key %s, got %s\n", 306 val, 307 arg, 308 sn.cstr); 309 return MHD_action_abort_request (request); 310 } 311 } 312 } 313 } 314 315 return MHD_action_from_response ( 316 request, 317 MHD_response_from_empty ( 318 MHD_HTTP_STATUS_NO_CONTENT)); 319 } 320 321 322 const struct MHD_Action * 323 MHDT_server_reply_check_header ( 324 void *cls, 325 struct MHD_Request *MHD_RESTRICT request, 326 const struct MHD_String *MHD_RESTRICT path, 327 enum MHD_HTTP_Method method, 328 uint_fast64_t upload_size) 329 { 330 const char *want = cls; 331 size_t wlen = strlen (want) + 1; 332 char key[wlen]; 333 const char *colon = strchr (want, ':'); 334 struct MHD_StringNullable have; 335 const char *value; 336 337 (void)path; 338 (void)method; 339 (void)upload_size; /* Unused */ 340 341 memcpy (key, 342 want, 343 wlen); 344 if (NULL != colon) 345 { 346 key[colon - want] = '\0'; 347 value = &key[colon - want + 1]; 348 } 349 else 350 { 351 value = NULL; 352 } 353 if (MHD_NO == 354 MHD_request_get_value (request, 355 MHD_VK_HEADER, 356 key, 357 &have)) 358 { 359 fprintf (stderr, 360 "Missing client header `%s'\n", 361 want); 362 return MHD_action_abort_request (request); 363 } 364 if (NULL == value) 365 { 366 if (NULL != have.cstr) 367 { 368 fprintf (stderr, 369 "Have unexpected client header `%s': `%s'\n", 370 key, 371 have.cstr); 372 return MHD_action_abort_request (request); 373 } 374 } 375 else 376 { 377 if (NULL == have.cstr) 378 { 379 fprintf (stderr, 380 "Missing value for client header `%s'\n", 381 want); 382 return MHD_action_abort_request (request); 383 } 384 if (0 != strcmp (have.cstr, 385 value)) 386 { 387 fprintf (stderr, 388 "Client HTTP header `%s' was expected to be `%s' but is `%s'\n", 389 key, 390 value, 391 have.cstr); 392 return MHD_action_abort_request (request); 393 } 394 } 395 return MHD_action_from_response ( 396 request, 397 MHD_response_from_empty ( 398 MHD_HTTP_STATUS_NO_CONTENT)); 399 } 400 401 402 /** 403 * Function to process data uploaded by a client. 404 * 405 * @param cls the payload we expect to be uploaded as a 0-terminated string 406 * @param request the request is being processed 407 * @param content_data_size the size of the @a content_data, 408 * zero when all data have been processed 409 * @param[in] content_data the uploaded content data, 410 * may be modified in the callback, 411 * valid only until return from the callback, 412 * NULL when all data have been processed 413 * @return action specifying how to proceed: 414 * #MHD_upload_action_continue() to continue upload (for incremental 415 * upload processing only), 416 * #MHD_upload_action_suspend() to stop reading the upload until 417 * the request is resumed, 418 * #MHD_upload_action_abort_request() to close the socket, 419 * or a response to discard the rest of the upload and transmit 420 * the response 421 * @ingroup action 422 */ 423 static const struct MHD_UploadAction * 424 check_upload_cb (void *cls, 425 struct MHD_Request *request, 426 size_t content_data_size, 427 void *content_data) 428 { 429 const char *want = cls; 430 size_t wlen = strlen (want); 431 432 if (content_data_size != wlen) 433 { 434 fprintf (stderr, 435 "Invalid body size given to full upload callback\n"); 436 return MHD_upload_action_abort_request (request); 437 } 438 if (0 != memcmp (want, 439 content_data, 440 wlen)) 441 { 442 fprintf (stderr, 443 "Invalid body data given to full upload callback\n"); 444 return MHD_upload_action_abort_request (request); 445 } 446 /* success! */ 447 return MHD_upload_action_from_response ( 448 request, 449 MHD_response_from_empty ( 450 MHD_HTTP_STATUS_NO_CONTENT)); 451 } 452 453 454 const struct MHD_Action * 455 MHDT_server_reply_check_upload ( 456 void *cls, 457 struct MHD_Request *MHD_RESTRICT request, 458 const struct MHD_String *MHD_RESTRICT path, 459 enum MHD_HTTP_Method method, 460 uint_fast64_t upload_size) 461 { 462 const char *want = cls; 463 size_t wlen = strlen (want); 464 465 (void)path; 466 (void)method; 467 (void)upload_size; /* Unused */ 468 469 return MHD_action_process_upload_full (request, 470 wlen, 471 &check_upload_cb, 472 (void *)want); 473 } 474 475 476 /** 477 * Closure for #chunk_return. 478 */ 479 struct ChunkContext 480 { 481 /** 482 * Where we are in the buffer. 483 */ 484 const char *pos; 485 }; 486 487 488 /** 489 * Function that returns a string in chunks. 490 * 491 * @param dyn_cont_cls must be a `struct ChunkContext` 492 * @param ctx the context to produce the action to return, 493 * the pointer is only valid until the callback returns 494 * @param pos position in the datastream to access; 495 * note that if a `struct MHD_Response` object is re-used, 496 * it is possible for the same content reader to 497 * be queried multiple times for the same data; 498 * however, if a `struct MHD_Response` is not re-used, 499 * libmicrohttpd guarantees that "pos" will be 500 * the sum of all data sizes provided by this callback 501 * @param[out] buf where to copy the data 502 * @param max maximum number of bytes to copy to @a buf (size of @a buf) 503 * @return action to use, 504 * NULL in case of any error (the response will be aborted) 505 */ 506 static const struct MHD_DynamicContentCreatorAction * 507 chunk_return (void *cls, 508 struct MHD_DynamicContentCreatorContext *ctx, 509 uint_fast64_t pos, 510 void *buf, 511 size_t max) 512 { 513 struct ChunkContext *cc = cls; 514 size_t imax = strlen (cc->pos); 515 const char *space = strchr (cc->pos, ' '); 516 517 (void)pos; // TODO: add check 518 519 if (0 == imax) 520 return MHD_DCC_action_finish (ctx); 521 if (NULL != space) 522 imax = (size_t)(space - cc->pos) + 1; 523 if (imax > max) 524 imax = max; 525 memcpy (buf, 526 cc->pos, 527 imax); 528 cc->pos += imax; 529 return MHD_DCC_action_continue (ctx, 530 imax); 531 } 532 533 534 const struct MHD_Action * 535 MHDT_server_reply_chunked_text ( 536 void *cls, 537 struct MHD_Request *MHD_RESTRICT request, 538 const struct MHD_String *MHD_RESTRICT path, 539 enum MHD_HTTP_Method method, 540 uint_fast64_t upload_size) 541 { 542 const char *text = cls; 543 struct ChunkContext *cc; 544 545 (void)path; 546 (void)method; 547 (void)upload_size; /* Unused */ 548 549 cc = malloc (sizeof (struct ChunkContext)); 550 if (NULL == cc) 551 return NULL; 552 cc->pos = text; 553 554 return MHD_action_from_response ( 555 request, 556 MHD_response_from_callback (MHD_HTTP_STATUS_OK, 557 MHD_SIZE_UNKNOWN, 558 &chunk_return, 559 cc, 560 &free)); 561 } 562 563 564 /** 565 * Compare two strings, succeed if both are NULL. 566 * 567 * @param wants string we want 568 * @param have string we have 569 * @return true if what we @a want is what we @a have 570 */ 571 static bool 572 nstrcmp (const char *wants, 573 const struct MHD_StringNullable *have) 574 { 575 if ((NULL == wants) 576 && (NULL == have->cstr) 577 && (0 == have->len)) 578 return true; 579 if ((NULL == wants) 580 || (NULL == have->cstr)) 581 return false; 582 return (0 == strcmp (wants, 583 have->cstr)); 584 } 585 586 587 /** 588 * "Stream" reader for POST data. 589 * This callback is called to incrementally process parsed POST data sent by 590 * the client. 591 * 592 * @param req the request 593 * @param cls user-specified closure 594 * @param name the name of the POST field 595 * @param filename the name of the uploaded file, @a cstr member is NULL if not 596 * known / not provided 597 * @param content_type the mime-type of the data, cstr member is NULL if not 598 * known / not provided 599 * @param encoding the encoding of the data, cstr member is NULL if not known / 600 * not provided 601 * @param size the number of bytes in @a data available, may be zero if 602 * the @a final_data is #MHD_YES 603 * @param data the pointer to @a size bytes of data at the specified 604 * @a off offset, NOT zero-terminated 605 * @param off the offset of @a data in the overall value, always equal to 606 * the sum of sizes of previous calls for the same field / file; 607 * client may provide more than one field with the same name and 608 * the same filename, the new filed (or file) is indicated by zero 609 * value of @a off (and the end is indicated by @a final_data) 610 * @param final_data if set to #MHD_YES then full field data is provided, 611 * if set to #MHD_NO then more field data may be provided 612 * @return action specifying how to proceed: 613 * #MHD_upload_action_continue() if all is well, 614 * #MHD_upload_action_suspend() to stop reading the upload until 615 * the request is resumed, 616 * #MHD_upload_action_abort_request() to close the socket, 617 * or a response to discard the rest of the upload and transmit 618 * the response 619 * @ingroup action 620 */ 621 static const struct MHD_UploadAction * 622 post_stream_reader (struct MHD_Request *req, 623 void *cls, 624 const struct MHD_String *name, 625 const struct MHD_StringNullable *filename, 626 const struct MHD_StringNullable *content_type, 627 const struct MHD_StringNullable *encoding, 628 size_t size, 629 const void *data, 630 uint_fast64_t off, 631 enum MHD_Bool final_data) 632 { 633 struct MHDT_PostInstructions *pi = cls; 634 struct MHDT_PostWant *wants = pi->wants; 635 636 (void)encoding; // TODO: add check 637 638 if (NULL != wants) 639 { 640 for (unsigned int i = 0; NULL != wants[i].key; i++) 641 { 642 struct MHDT_PostWant *want = &wants[i]; 643 644 if (want->satisfied) 645 continue; 646 if (0 != strcmp (want->key, 647 name->cstr)) 648 continue; 649 if (!nstrcmp (want->filename, 650 filename)) 651 continue; 652 if (!nstrcmp (want->content_type, 653 content_type)) 654 continue; 655 if (!want->incremental) 656 continue; 657 if (want->value_off != off) 658 continue; 659 if (want->value_size < off + size) 660 continue; 661 if (0 != memcmp (data, 662 want->value + off, 663 size)) 664 continue; 665 want->value_off += size; 666 want->satisfied = (want->value_size == want->value_off) && final_data; 667 } 668 } 669 670 return MHD_upload_action_continue (req); 671 } 672 673 674 /** 675 * Iterator over name-value pairs. This iterator can be used to 676 * iterate over all of the cookies, headers, or POST-data fields of a 677 * request, and also to iterate over the headers that have been added 678 * to a response. 679 * 680 * The pointers to the strings in @a nvt are valid until the response 681 * is queued. If the data is needed beyond this point, it should be copied. 682 * 683 * @param cls closure 684 * @param nvt the name, the value and the kind of the element 685 * @return #MHD_YES to continue iterating, 686 * #MHD_NO to abort the iteration 687 * @ingroup request 688 */ 689 static enum MHD_Bool 690 check_complete_post_value ( 691 void *cls, 692 enum MHD_ValueKind kind, 693 const struct MHD_NameAndValue *nv) 694 { 695 struct MHDT_PostInstructions *pi = cls; 696 struct MHDT_PostWant *wants = pi->wants; 697 698 if (NULL == wants) 699 return MHD_NO; 700 if (MHD_VK_POSTDATA != kind) 701 return MHD_NO; 702 for (unsigned int i = 0; NULL != wants[i].key; i++) 703 { 704 struct MHDT_PostWant *want = &wants[i]; 705 706 if (want->satisfied) 707 continue; 708 if (want->incremental) 709 continue; 710 if (0 != strcmp (want->key, 711 nv->name.cstr)) 712 continue; 713 if (NULL == want->value) 714 { 715 if (NULL == nv->value.cstr) 716 want->satisfied = true; 717 } 718 else if (NULL == nv->value.cstr) 719 continue; 720 else if (0 == want->value_size) 721 { 722 if (0 == strcmp (nv->value.cstr, 723 want->value)) 724 want->satisfied = true; 725 } 726 else 727 { 728 if ((want->value_size == nv->value.len) 729 && (0 == memcmp (nv->value.cstr, 730 want->value, 731 want->value_size))) 732 want->satisfied = true; 733 } 734 } 735 return MHD_YES; 736 } 737 738 739 /** 740 * The callback to be called when finished with processing 741 * of the postprocessor upload data. 742 * @param req the request 743 * @param cls the closure 744 * @param parsing_result the result of POST data parsing 745 * @return the action to proceed 746 */ 747 static const struct MHD_UploadAction * 748 post_stream_done (struct MHD_Request *req, 749 void *cls, 750 enum MHD_PostParseResult parsing_result) 751 { 752 struct MHDT_PostInstructions *pi = cls; 753 struct MHDT_PostWant *wants = pi->wants; 754 755 if (MHD_POST_PARSE_RES_OK != parsing_result) 756 { 757 fprintf (stderr, 758 "POST parsing was not successful. The result: %d\n", 759 (int)parsing_result); 760 return MHD_upload_action_abort_request (req); 761 } 762 763 MHD_request_get_values_cb (req, 764 MHD_VK_POSTDATA, 765 &check_complete_post_value, 766 pi); 767 if (NULL != wants) 768 { 769 for (unsigned int i = 0; NULL != wants[i].key; i++) 770 { 771 struct MHDT_PostWant *want = &wants[i]; 772 773 if (want->satisfied) 774 continue; 775 fprintf (stderr, 776 "Expected key-value pair `%s' missing\n", 777 want->key); 778 return MHD_upload_action_abort_request (req); 779 } 780 } 781 return MHD_upload_action_from_response ( 782 req, 783 MHD_response_from_empty ( 784 MHD_HTTP_STATUS_NO_CONTENT)); 785 } 786 787 788 const struct MHD_Action * 789 MHDT_server_reply_check_post ( 790 void *cls, 791 struct MHD_Request *MHD_RESTRICT request, 792 const struct MHD_String *MHD_RESTRICT path, 793 enum MHD_HTTP_Method method, 794 uint_fast64_t upload_size) 795 { 796 struct MHDT_PostInstructions *pi = cls; 797 798 (void)path; /* Unused */ 799 (void)upload_size; // TODO: add check 800 801 if (MHD_HTTP_METHOD_POST != method) 802 { 803 fprintf (stderr, 804 "Reported HTTP method other then POST. Reported method: %u\n", 805 (unsigned)method); 806 return MHD_action_abort_request (req); 807 } 808 809 return MHD_action_parse_post (request, 810 pi->buffer_size, 811 pi->auto_stream_size, 812 pi->enc, 813 &post_stream_reader, 814 pi, 815 &post_stream_done, 816 pi); 817 } 818 819 820 const struct MHD_Action * 821 MHDT_server_reply_check_basic_auth ( 822 void *cls, 823 struct MHD_Request *MHD_RESTRICT request, 824 const struct MHD_String *MHD_RESTRICT path, 825 enum MHD_HTTP_Method method, 826 uint_fast64_t upload_size) 827 { 828 const char *cred = cls; 829 union MHD_RequestInfoDynamicData dd; 830 enum MHD_StatusCode sc; 831 const struct MHD_AuthBasicCreds *ba; 832 833 /* should not be needed, except to make gcc happy */ 834 memset (&dd, 835 0, 836 sizeof (dd)); 837 sc = MHD_request_get_info_dynamic (request, 838 MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS, 839 &dd); 840 if (MHD_SC_OK != sc) 841 { 842 fprintf (stderr, 843 "No credentials?\n"); 844 return MHD_action_basic_auth_challenge_p ( 845 request, 846 "test-realm", 847 MHD_YES, 848 MHD_response_from_empty ( 849 MHD_HTTP_STATUS_UNAUTHORIZED)); 850 } 851 ba = dd.v_auth_basic_creds; 852 assert (NULL != ba); 853 if ((0 != strncmp (ba->username.cstr, 854 cred, 855 ba->username.len)) 856 || (':' != cred[ba->username.len]) 857 || (NULL == ba->password.cstr) 858 || (0 != strcmp (ba->password.cstr, 859 &cred[ba->username.len + 1]))) 860 { 861 fprintf (stderr, 862 "Wrong credentials (Got: %s/%s Want: %s)!\n", 863 ba->username.cstr, 864 ba->password.cstr, 865 cred); 866 return MHD_action_basic_auth_challenge_p ( 867 request, 868 "test-realm", 869 MHD_YES, 870 MHD_response_from_empty ( 871 MHD_HTTP_STATUS_UNAUTHORIZED)); 872 } 873 return MHD_action_from_response ( 874 request, 875 MHD_response_from_empty ( 876 MHD_HTTP_STATUS_NO_CONTENT)); 877 } 878 879 880 const struct MHD_Action * 881 MHDT_server_reply_check_digest_auth ( 882 void *cls, 883 struct MHD_Request *MHD_RESTRICT request, 884 const struct MHD_String *MHD_RESTRICT path, 885 enum MHD_HTTP_Method method, 886 uint_fast64_t upload_size) 887 { 888 const char *cred = cls; 889 const char *colon = strchr (cred, ':'); 890 char *username; 891 const char *password; 892 enum MHD_DigestAuthResult dar; 893 const char *realm = "test-realm"; 894 #if CURL_AT_LEAST_VERSION (7, 57, 0) 895 enum MHD_DigestAuthAlgo algo = MHD_DIGEST_AUTH_ALGO_SHA256; 896 #else 897 enum MHD_DigestAuthAlgo algo = MHD_DIGEST_AUTH_ALGO_MD5; 898 #endif 899 size_t digest_len = MHD_digest_get_hash_size (algo); 900 901 (void)cls; /* Unused, mute compiler warning */ 902 903 if (0 == digest_len) 904 return NULL; 905 assert (NULL != colon); 906 password = colon + 1; 907 username = strndup (cred, 908 colon - cred); 909 assert (NULL != username); 910 { 911 enum MHD_StatusCode sc; 912 char digest[digest_len]; 913 914 // FIXME: why is this needed? We should not get a warning 915 // even without this memset! 916 memset (digest, 0, sizeof (digest)); 917 sc = MHD_digest_auth_calc_userdigest (algo, 918 username, 919 realm, 920 password, 921 sizeof (digest), 922 digest); 923 if (MHD_SC_OK != sc) 924 { 925 fprintf (stderr, 926 "MHD_digest_auth_calc_userdigest: %d\n", 927 (int)sc); 928 free (username); 929 return NULL; 930 } 931 dar = MHD_digest_auth_check_digest (request, 932 realm, 933 username, 934 sizeof (digest), 935 digest, 936 0, /* maximum nonce counter; 0: default */ 937 MHD_DIGEST_AUTH_MULT_QOP_AUTH, 938 (enum MHD_DigestAuthMultiAlgo)algo); 939 } 940 free (username); 941 if ((MHD_DAUTH_HEADER_MISSING == dar) 942 || (MHD_DAUTH_NONCE_STALE == dar)) 943 { 944 struct MHD_Response *resp; 945 enum MHD_StatusCode sc; 946 947 resp = MHD_response_from_empty ( 948 MHD_HTTP_STATUS_UNAUTHORIZED); 949 if (NULL == resp) 950 { 951 fprintf (stderr, 952 "Failed to create response body\n"); 953 return NULL; 954 } 955 sc = MHD_response_add_auth_digest_challenge ( 956 resp, 957 "test-realm", 958 "opaque", 959 NULL, /* domain */ 960 (MHD_DAUTH_NONCE_STALE == dar) ? MHD_YES : MHD_NO, /* indicate stale */ 961 MHD_DIGEST_AUTH_MULT_QOP_AUTH, 962 (enum MHD_DigestAuthMultiAlgo)algo, 963 MHD_NO /* userhash_support */, 964 MHD_YES /* prefer UTF8 */); 965 if (MHD_SC_OK != sc) 966 { 967 fprintf (stderr, 968 "MHD_response_add_auth_digest_challenge failed: %d\n", 969 (int)sc); 970 return NULL; 971 } 972 return MHD_action_from_response ( 973 request, 974 resp); 975 } 976 if (MHD_DAUTH_RESPONSE_WRONG == dar) 977 return MHD_action_from_response ( 978 request, 979 MHD_response_from_empty (MHD_HTTP_STATUS_FORBIDDEN)); 980 981 if (MHD_DAUTH_OK == dar) 982 return MHD_action_from_response ( 983 request, 984 MHD_response_from_empty ( 985 MHD_HTTP_STATUS_NO_CONTENT)); 986 987 return MHD_action_abort_request (request); 988 }