| 634 | } |
| 635 | |
| 636 | int ff_parse_opts_from_query_string(void *obj, const char *str, int allow_unknown) |
| 637 | { |
| 638 | const AVOption *opt; |
| 639 | char optval[512]; |
| 640 | int ret; |
| 641 | |
| 642 | if (*str == '?') |
| 643 | str++; |
| 644 | while (*str) { |
| 645 | size_t len = strcspn(str, "=&"); |
| 646 | opt = find_opt(obj, str, len); |
| 647 | if (!opt) { |
| 648 | if (!allow_unknown) { |
| 649 | av_log(obj, AV_LOG_ERROR, "Query string option '%.*s' does not exist\n", (int)len, str); |
| 650 | return AVERROR_OPTION_NOT_FOUND; |
| 651 | } |
| 652 | av_log(obj, AV_LOG_VERBOSE, "Ignoring unknown query string option '%.*s'\n", (int)len, str); |
| 653 | } |
| 654 | str += len; |
| 655 | if (!opt) { |
| 656 | len = strcspn(str, "&"); |
| 657 | str += len; |
| 658 | } else if (*str == '&' || *str == '\0') { |
| 659 | /* Check for bool options without value, e.g. "?verify". |
| 660 | * Unfortunately "listen" is a tri-state INT for some protocols so |
| 661 | * we also have to allow that for backward compatibility. */ |
| 662 | if (opt->type != AV_OPT_TYPE_BOOL && strcmp(opt->name, "listen")) { |
| 663 | av_log(obj, AV_LOG_ERROR, "Non-bool query string option '%s' has no value\n", opt->name); |
| 664 | return AVERROR(EINVAL); |
| 665 | } |
| 666 | ret = av_opt_set_int(obj, opt->name, 1, 0); |
| 667 | if (ret < 0) |
| 668 | return ret; |
| 669 | } else { |
| 670 | av_assert2(*str == '='); |
| 671 | str++; |
| 672 | len = strcspn(str, "&"); |
| 673 | if (ff_urldecode_len(optval, sizeof(optval), str, len, 1) < 0) { |
| 674 | av_log(obj, AV_LOG_ERROR, "Query string option '%s' value is too long\n", opt->name); |
| 675 | return AVERROR(EINVAL); |
| 676 | } |
| 677 | ret = av_opt_set(obj, opt->name, optval, 0); |
| 678 | if (ret < 0) |
| 679 | return ret; |
| 680 | str += len; |
| 681 | } |
| 682 | if (*str) |
| 683 | str++; |
| 684 | } |
| 685 | return 0; |
| 686 | } |
no test coverage detected