Skip to content
Snippets Groups Projects
ffserver.c 125 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (len < 0) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return -1;
    
            /* search for end of request. */
            c->buffer_ptr += len;
            ptr = c->buffer_ptr;
            if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
                (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
                /* request found : parse it and reply */
    
                if (c->state == HTTPSTATE_WAIT_REQUEST)
    
                    ret = http_parse_request(c);
    
                    ret = rtsp_parse_request(c);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return -1;
    
            } else if (ptr >= c->buffer_end) {
                /* request too long: cannot do anything */
                return -1;
            } else goto read_loop;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
        case HTTPSTATE_SEND_HEADER:
            if (c->poll_entry->revents & (POLLERR | POLLHUP))
                return -1;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
    
            len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (len < 0) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR)) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
    
            c->buffer_ptr += len;
            if (c->stream)
                c->stream->bytes_served += len;
            c->data_count += len;
            if (c->buffer_ptr >= c->buffer_end) {
                av_freep(&c->pb_buffer);
                /* if error, exit */
                if (c->http_error)
                    return -1;
                /* all the buffer was sent : synchronize to the incoming
                 * stream */
                c->state = HTTPSTATE_SEND_DATA_HEADER;
                c->buffer_ptr = c->buffer_end = c->buffer;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
        case HTTPSTATE_SEND_DATA:
        case HTTPSTATE_SEND_DATA_HEADER:
        case HTTPSTATE_SEND_DATA_TRAILER:
    
            /* for packetized output, we consider we can always write (the
    
             * input streams set the speed). It may be better to verify
             * that we do not rely too much on the kernel queues */
    
            if (!c->is_packetized) {
                if (c->poll_entry->revents & (POLLERR | POLLHUP))
                    return -1;
    
                /* no need to read if no events */
                if (!(c->poll_entry->revents & POLLOUT))
                    return 0;
            }
    
            if (http_send_data(c) < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
            /* close connection if trailer sent */
            if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
                return -1;
    
            /* Check if it is a single jpeg frame 123 */
            if (c->stream->single_frame && c->data_count > c->cur_frame_bytes && c->cur_frame_bytes > 0) {
                close_connection(c);
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
        case HTTPSTATE_RECEIVE_DATA:
            /* no need to read if no events */
            if (c->poll_entry->revents & (POLLERR | POLLHUP))
                return -1;
            if (!(c->poll_entry->revents & POLLIN))
                return 0;
            if (http_receive_data(c) < 0)
                return -1;
            break;
        case HTTPSTATE_WAIT_FEED:
            /* no need to read if no events */
    
            if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
            /* nothing to do, we'll be waken up by incoming feed packets */
            break;
    
            if (c->poll_entry->revents & (POLLERR | POLLHUP))
                goto close_connection;
    
            /* no need to write if no events */
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
    
            len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR)) {
    
            c->buffer_ptr += len;
            c->data_count += len;
            if (c->buffer_ptr >= c->buffer_end) {
                /* all the buffer was sent : wait for a new request */
                av_freep(&c->pb_buffer);
                start_wait_request(c, 1);
            }
    
        case RTSPSTATE_SEND_PACKET:
            if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
                av_freep(&c->packet_buffer);
                return -1;
            }
            /* no need to write if no events */
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
    
            len = send(c->fd, c->packet_buffer_ptr,
                        c->packet_buffer_end - c->packet_buffer_ptr, 0);
    
            if (len < 0) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR)) {
    
                    /* error : close connection */
                    av_freep(&c->packet_buffer);
                    return -1;
                }
    
            c->packet_buffer_ptr += len;
            if (c->packet_buffer_ptr >= c->packet_buffer_end) {
                /* all the buffer was sent : wait for a new request */
                av_freep(&c->packet_buffer);
                c->state = RTSPSTATE_WAIT_REQUEST;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        default:
            return -1;
        }
        return 0;
    
    
    close_connection:
        av_freep(&c->pb_buffer);
        return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    
    static int extract_rates(char *rates, int ratelen, const char *request)
    {
        const char *p;
    
        for (p = request; *p && *p != '\r' && *p != '\n'; ) {
    
            if (av_strncasecmp(p, "Pragma:", 7) == 0) {
    
                while (*q && *q != '\n' && av_isspace(*q))
    
                if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
    
                    memset(rates, 0xff, ratelen);
    
                        if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
    
                        if (stream_no < ratelen && stream_no >= 0)
    
                        while (*q && *q != '\n' && !av_isspace(*q))
    
    static int find_stream_in_feed(FFServerStream *feed, AVCodecParameters *codec,
    
        int best_bitrate = 100000000;
        int best = -1;
    
        for (i = 0; i < feed->nb_streams; i++) {
    
            AVCodecParameters *feed_codec = feed->streams[i]->codecpar;
    
    
            if (feed_codec->codec_id != codec->codec_id ||
                feed_codec->sample_rate != codec->sample_rate ||
                feed_codec->width != codec->width ||
    
                feed_codec->height != codec->height)
    
                continue;
    
            /* Potential stream */
    
    
            /* We want the fastest stream less than bit_rate, or the slowest
    
             * faster than bit_rate
             */
    
            if (feed_codec->bit_rate <= bit_rate) {
    
                if (best_bitrate > bit_rate ||
                    feed_codec->bit_rate > best_bitrate) {
    
                    best_bitrate = feed_codec->bit_rate;
                    best = i;
                }
    
                continue;
            }
            if (feed_codec->bit_rate < best_bitrate) {
                best_bitrate = feed_codec->bit_rate;
                best = i;
    
            }
        }
        return best;
    }
    
    static int modify_current_stream(HTTPContext *c, char *rates)
    {
        int i;
    
        FFServerStream *req = c->stream;
    
        int action_required = 0;
    
        /* Not much we can do for a feed */
        if (!req->feed)
            return 0;
    
    
        for (i = 0; i < req->nb_streams; i++) {
    
            AVCodecParameters *codec = req->streams[i]->codecpar;
    
                    c->switch_feed_streams[i] = req->feed_streams[i];
    
                    c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
    
                    /* Wants off or slow */
                    c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
    #ifdef WANTS_OFF
                    /* This doesn't work well when it turns off the only stream! */
                    c->switch_feed_streams[i] = -2;
                    c->feed_streams[i] = -2;
    #endif
    
            if (c->switch_feed_streams[i] >= 0 &&
                c->switch_feed_streams[i] != c->feed_streams[i]) {
    
                action_required = 1;
    
        return action_required;
    }
    
    static void get_word(char *buf, int buf_size, const char **pp)
    {
        const char *p;
        char *q;
    
    
        p += strspn(p, SPACE_CHARS);
    
        while (!av_isspace(*p) && *p != '\0') {
    
            if ((q - buf) < buf_size - 1)
                *q++ = *p;
            p++;
        }
        if (buf_size > 0)
            *q = '\0';
        *pp = p;
    }
    
    
    static FFServerIPAddressACL* parse_dynamic_acl(FFServerStream *stream,
                                                   HTTPContext *c)
    
    {
        FILE* f;
        char line[1024];
        char  cmd[1024];
    
        FFServerIPAddressACL *acl = NULL;
    
        int line_num = 0;
        const char *p;
    
        f = fopen(stream->dynamic_acl, "r");
        if (!f) {
            perror(stream->dynamic_acl);
            return NULL;
        }
    
    
        acl = av_mallocz(sizeof(FFServerIPAddressACL));
    
        if (!acl) {
            fclose(f);
            return NULL;
        }
    
        while (fgets(line, sizeof(line), f)) {
    
            line_num++;
            p = line;
    
            while (av_isspace(*p))
    
                p++;
            if (*p == '\0' || *p == '#')
                continue;
    
            ffserver_get_arg(cmd, sizeof(cmd), &p);
    
            if (!av_strcasecmp(cmd, "ACL"))
    
                ffserver_parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl,
                                       line_num);
    
    static void free_acl_list(FFServerIPAddressACL *in_acl)
    
        FFServerIPAddressACL *pacl, *pacl2;
    
    
        pacl = in_acl;
        while(pacl) {
            pacl2 = pacl;
            pacl = pacl->next;
            av_freep(pacl2);
        }
    }
    
    
    static int validate_acl_list(FFServerIPAddressACL *in_acl, HTTPContext *c)
    
        enum FFServerIPAddressAction last_action = IP_DENY;
        FFServerIPAddressACL *acl;
    
        struct in_addr *src = &c->from_addr.sin_addr;
    
        unsigned long src_addr = src->s_addr;
    
        for (acl = in_acl; acl; acl = acl->next) {
    
            if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
    
                return (acl->action == IP_ALLOW) ? 1 : 0;
            last_action = acl->action;
        }
    
        /* Nothing matched, so return not the last action */
        return (last_action == IP_DENY) ? 1 : 0;
    }
    
    
    static int validate_acl(FFServerStream *stream, HTTPContext *c)
    
        FFServerIPAddressACL *acl;
    
    
        /* if stream->acl is null validate_acl_list will return 1 */
        ret = validate_acl_list(stream->acl, c);
    
        if (stream->dynamic_acl[0]) {
            acl = parse_dynamic_acl(stream, c);
            ret = validate_acl_list(acl, c);
            free_acl_list(acl);
        }
    
        return ret;
    }
    
    
    /**
     * compute the real filename of a file by matching it without its
     * extensions to all the stream's filenames
     */
    
    static void compute_real_filename(char *filename, int max_size)
    {
        char file1[1024];
        char file2[1024];
        char *p;
    
        FFServerStream *stream;
    
        av_strlcpy(file1, filename, sizeof(file1));
    
        for(stream = config.first_stream; stream; stream = stream->next) {
    
            av_strlcpy(file2, stream->filename, sizeof(file2));
    
            p = strrchr(file2, '.');
            if (p)
                *p = '\0';
            if (!strcmp(file1, file2)) {
    
                av_strlcpy(filename, stream->filename, max_size);
    
    /* parse HTTP request and prepare header */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int http_parse_request(HTTPContext *c)
    {
    
        const char *p;
        char *p1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char cmd[32];
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char url[1024], *q;
        char protocol[32];
        char msg[1024];
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        const char *mime_type;
    
        FFServerStream *stream;
    
        const char *useragent = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        p = c->buffer;
    
        get_word(cmd, sizeof(cmd), &p);
    
        av_strlcpy(c->method, cmd, sizeof(c->method));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!strcmp(cmd, "GET"))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        else if (!strcmp(cmd, "POST"))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        else
            return -1;
    
    
        get_word(url, sizeof(url), &p);
    
        av_strlcpy(c->url, url, sizeof(c->url));
    
        get_word(protocol, sizeof(protocol), (const char **)&p);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
            return -1;
    
        av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
    
            http_log("%s - - New connection: %s %s\n",
                     inet_ntoa(c->from_addr.sin_addr), cmd, url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* find the filename and the optional info string in the request */
    
        p1 = strchr(url, '?');
        if (p1) {
            av_strlcpy(info, p1, sizeof(info));
            *p1 = '\0';
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            info[0] = '\0';
    
    
        av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
    
        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
            if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
    
                useragent = p + 11;
    
                if (*useragent && *useragent != '\n' && av_isspace(*useragent))
    
                    useragent++;
                break;
            }
            p = strchr(p, '\n');
            if (!p)
                break;
    
            p++;
        }
    
    
        if (av_match_ext(filename, "asx")) {
    
            filename[strlen(filename)-1] = 'f';
    
        } else if (av_match_ext(filename, "asf") &&
    
            (!useragent || av_strncasecmp(useragent, "NSPlayer", 8))) {
    
            /* if this isn't WMP or lookalike, return the redirector file */
    
        } else if (av_match_ext(filename, "rpm,ram")) {
    
            strcpy(filename + strlen(filename)-2, "m");
    
        } else if (av_match_ext(filename, "rtsp")) {
    
            compute_real_filename(filename, sizeof(filename) - 1);
    
        } else if (av_match_ext(filename, "sdp")) {
    
            compute_real_filename(filename, sizeof(filename) - 1);
    
        /* "redirect" request to index.html */
    
            av_strlcpy(filename, "index.html", sizeof(filename) - 1);
    
        stream = config.first_stream;
    
        while (stream) {
    
            if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                break;
            stream = stream->next;
        }
    
            snprintf(msg, sizeof(msg), "File '%s' not found", url);
    
            http_log("File '%s' not found\n", url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            goto send_error;
        }
    
        c->stream = stream;
        memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
        memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
    
        if (stream->stream_type == STREAM_TYPE_REDIRECT) {
            c->http_error = 301;
            q = c->buffer;
    
            snprintf(q, c->buffer_size,
    
                          "HTTP/1.0 301 Moved\r\n"
                          "Location: %s\r\n"
                          "Content-type: text/html\r\n"
                          "\r\n"
    
                          "<html><head><title>Moved</title></head><body>\r\n"
                          "You should be <a href=\"%s\">redirected</a>.\r\n"
    
                          "</body></html>\r\n",
                     stream->feed_filename, stream->feed_filename);
    
            /* prepare output buffer */
            c->buffer_ptr = c->buffer;
            c->buffer_end = q;
            c->state = HTTPSTATE_SEND_HEADER;
            return 0;
        }
    
    
        /* If this is WMP, get the rate information */
        if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
    
            if (modify_current_stream(c, ratebuf)) {
    
                for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
    
                    if (c->switch_feed_streams[i] >= 0)
    
    Reinhard Tartler's avatar
    Reinhard Tartler committed
                        c->switch_feed_streams[i] = -1;
    
        if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
            current_bandwidth += stream->bandwidth;
    
    
        /* If already streaming this feed, do not let another feeder start */
    
        if (stream->feed_opened) {
            snprintf(msg, sizeof(msg), "This feed is already being received.");
    
            http_log("Feed '%s' already being received\n", stream->feed_filename);
    
        if (c->post == 0 && config.max_bandwidth < current_bandwidth) {
    
            c->http_error = 503;
    
            snprintf(q, c->buffer_size,
    
                          "HTTP/1.0 503 Server too busy\r\n"
    
                          "Content-type: text/html\r\n"
                          "\r\n"
    
                          "<html><head><title>Too busy</title></head><body>\r\n"
    
                          "<p>The server is too busy to serve your request at "
                          "this time.</p>\r\n"
                          "<p>The bandwidth being served (including your stream) "
    
                          "is %"PRIu64"kbit/s, and this exceeds the limit of "
                          "%"PRIu64"kbit/s.</p>\r\n"
    
                          "</body></html>\r\n",
                     current_bandwidth, config.max_bandwidth);
    
            /* prepare output buffer */
            c->buffer_ptr = c->buffer;
            c->buffer_end = q;
            c->state = HTTPSTATE_SEND_HEADER;
            return 0;
        }
    
            const char *hostinfo = 0;
    
            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
                if (av_strncasecmp(p, "Host:", 5) == 0) {
    
                    hostinfo = p + 5;
                    break;
                }
                p = strchr(p, '\n');
                if (!p)
                    break;
    
                p++;
            }
    
            if (hostinfo) {
                char *eoh;
                char hostbuf[260];
    
    
                while (av_isspace(*hostinfo))
    
                    hostinfo++;
    
                eoh = strchr(hostinfo, '\n');
                if (eoh) {
                    if (eoh[-1] == '\r')
                        eoh--;
    
                    if (eoh - hostinfo < sizeof(hostbuf) - 1) {
                        memcpy(hostbuf, hostinfo, eoh - hostinfo);
                        hostbuf[eoh - hostinfo] = 0;
    
                        c->http_error = 200;
                        q = c->buffer;
    
                            snprintf(q, c->buffer_size,
    
                                          "HTTP/1.0 200 ASX Follows\r\n"
                                          "Content-type: video/x-ms-asf\r\n"
                                          "\r\n"
                                          "<ASX Version=\"3\">\r\n"
                                          //"<!-- Autogenerated by ffserver -->\r\n"
                                          "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
                                          "</ASX>\r\n", hostbuf, filename, info);
    
                            snprintf(q, c->buffer_size,
    
                                          "HTTP/1.0 200 RAM Follows\r\n"
                                          "Content-type: audio/x-pn-realaudio\r\n"
                                          "\r\n"
                                          "# Autogenerated by ffserver\r\n"
                                          "http://%s/%s%s\r\n", hostbuf, filename, info);
    
                            snprintf(q, c->buffer_size,
    
                                          "HTTP/1.0 200 ASF Redirect follows\r\n"
                                          "Content-type: video/x-ms-asf\r\n"
                                          "\r\n"
                                          "[Reference]\r\n"
                                          "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
    
                            break;
                        case REDIR_RTSP:
                            {
                                char hostname[256], *p;
                                /* extract only hostname */
    
                                av_strlcpy(hostname, hostbuf, sizeof(hostname));
    
                                snprintf(q, c->buffer_size,
    
                                              "HTTP/1.0 200 RTSP Redirect follows\r\n"
    
                                              /* XXX: incorrect MIME type ? */
    
                                              "Content-type: application/x-rtsp\r\n"
                                              "\r\n"
    
                                              "rtsp://%s:%d/%s\r\n", hostname, ntohs(config.rtsp_addr.sin_port), filename);
    
                                uint8_t *sdp_data;
    
                                int sdp_data_size;
                                socklen_t len;
    
                                snprintf(q, c->buffer_size,
    
                                              "HTTP/1.0 200 OK\r\n"
                                              "Content-type: application/sdp\r\n"
                                              "\r\n");
    
    
                                /* XXX: Should probably fail? */
                                if (getsockname(c->fd, (struct sockaddr *)&my_addr, &len))
                                    http_log("getsockname() failed\n");
    
                                sdp_data_size = prepare_sdp_description(stream,
                                                                        &sdp_data,
    
                                                                        my_addr.sin_addr);
                                if (sdp_data_size > 0) {
                                    memcpy(q, sdp_data, sdp_data_size);
                                    q += sdp_data_size;
                                    *q = '\0';
    
    
                        /* prepare output buffer */
                        c->buffer_ptr = c->buffer;
                        c->buffer_end = q;
                        c->state = HTTPSTATE_SEND_HEADER;
                        return 0;
                    }
                }
            }
    
    
            snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* XXX: add there authenticate and IP match */
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* if post, it means a feed is being sent */
            if (!stream->is_feed) {
    
    Diego Biurrun's avatar
    Diego Biurrun committed
                /* However it might be a status report from WMP! Let us log the
    
                 * data as it might come handy one day. */
    
                const char *logline = 0;
    
                for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
                    if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
    
                    if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
    
                    p = strchr(p, '\n');
                    if (!p)
                        break;
    
                    p++;
                }
    
                if (logline) {
                    char *eol = strchr(logline, '\n');
    
                    logline += 17;
    
                    if (eol) {
                        if (eol[-1] == '\r')
                            eol--;
    
    Falk Hüffner's avatar
    Falk Hüffner committed
                        http_log("%.*s\n", (int) (eol - logline), logline);
    
                http_log("\nGot request:\n%s\n", c->buffer);
    
    #endif
    
                if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
                    HTTPContext *wmpc;
    
                    /* Now we have to find the client_id */
                    for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
                        if (wmpc->wmp_client_id == client_id)
                            break;
                    }
    
    
                    if (wmpc && modify_current_stream(wmpc, ratebuf))
                        wmpc->switch_pending = 1;
    
                snprintf(msg, sizeof(msg), "POST command not handled");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                goto send_error;
            }
            if (http_start_receive_data(c) < 0) {
    
                snprintf(msg, sizeof(msg), "could not open feed");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                goto send_error;
            }
            c->http_error = 0;
            c->state = HTTPSTATE_RECEIVE_DATA;
            return 0;
        }
    
    
        if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
    
            http_log("\nGot request:\n%s\n", c->buffer);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (c->stream->stream_type == STREAM_TYPE_STATUS)
    
            goto send_status;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* open input stream */
        if (open_input_stream(c, info) < 0) {
    
            snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            goto send_error;
        }
    
    
        /* prepare HTTP header */
    
        c->buffer[0] = 0;
        av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        mime_type = c->stream->fmt->mime_type;
        if (!mime_type)
    
            mime_type = "application/x-octet-stream";
    
        av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* for asf, we need extra headers */
    
        if (!strcmp(c->stream->fmt->name,"asf_stream")) {
    
            c->wmp_client_id = av_lfg_get(&random_state);
    
            av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
        av_strlcatf(c->buffer, c->buffer_size, "\r\n");
        q = c->buffer + strlen(c->buffer);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* prepare output buffer */
        c->http_error = 0;
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
     send_error:
        c->http_error = 404;
        q = c->buffer;
    
        if (!htmlencode(msg, &encoded_msg)) {
            http_log("Could not encode filename '%s' as HTML\n", msg);
        }
    
        snprintf(q, c->buffer_size,
    
                      "HTTP/1.0 404 Not Found\r\n"
                      "Content-type: text/html\r\n"
                      "\r\n"
    
                      "<title>404 Not Found</title>\n"
                      "</head>\n"
    
                      "<body>%s</body>\n"
    
                      "</html>\n", encoded_msg? encoded_msg : "File not found");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return 0;
    
     send_status:
        compute_status(c);
    
        /* horrible: we use this value to avoid
         * going to the send data state */
        c->http_error = 200;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }
    
    
    static void fmt_bytecount(AVIOContext *pb, int64_t count)
    
        static const char suffix[] = " kMGTP";
    
        for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
    
        avio_printf(pb, "%"PRId64"%c", count, *s);
    
    static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream)
    {
        int i, stream_no;
        const char *type = "unknown";
        char parameters[64];
    
        AVCodec *codec;
    
        stream_no = stream->nb_streams;
    
    
        avio_printf(pb, "<table><tr><th>Stream<th>"
                        "type<th>kbit/s<th>codec<th>"
    
                        "Parameters\n");
    
        for (i = 0; i < stream_no; i++) {
            st = stream->streams[i];
    
            codec = avcodec_find_encoder(st->codecpar->codec_id);
    
            case AVMEDIA_TYPE_AUDIO:
                type = "audio";
                snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz",
    
                         st->codecpar->channels, st->codecpar->sample_rate);
    
                break;
            case AVMEDIA_TYPE_VIDEO:
                type = "video";
                snprintf(parameters, sizeof(parameters),
    
                         "%dx%d, q=%d-%d, fps=%d", st->codecpar->width,
                         st->codecpar->height, st->codec->qmin, st->codec->qmax,
    
            avio_printf(pb, "<tr><td>%d<td>%s<td>%"PRId64
    
                        i, type, (int64_t)st->codecpar->bit_rate/1000,
    
                        codec ? codec->name : "", parameters);
         }
    
         avio_printf(pb, "</table>\n");
    }
    
    
    static void clean_html(char *clean, int clean_len, char *dirty)
    {
        int i, o;
    
        for (o = i = 0; o+10 < clean_len && dirty[i];) {
            int len = strspn(dirty+i, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$-_.+!*(),?/ :;%");
            if (len) {
                if (o + len >= clean_len)
                    break;
                memcpy(clean + o, dirty + i, len);
                i += len;
                o += len;
            } else {
                int c = dirty[i++];
                switch (c) {
                case  '&': av_strlcat(clean+o, "&amp;"  , clean_len - o); break;
                case  '<': av_strlcat(clean+o, "&lt;"   , clean_len - o); break;
                case  '>': av_strlcat(clean+o, "&gt;"   , clean_len - o); break;
                case '\'': av_strlcat(clean+o, "&apos;" , clean_len - o); break;
                case '\"': av_strlcat(clean+o, "&quot;" , clean_len - o); break;
                default:   av_strlcat(clean+o, "&#9785;", clean_len - o); break;
                }
                o += strlen(clean+o);
            }
        }
        clean[o] = 0;
    }
    
    
    static void compute_status(HTTPContext *c)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        HTTPContext *c1;
    
        FFServerStream *stream;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        time_t ti;
    
        AVIOContext *pb;
    
        if (avio_open_dyn_buf(&pb) < 0) {
    
            c->buffer_ptr = c->buffer;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        avio_printf(pb, "HTTP/1.0 200 OK\r\n");
    
        avio_printf(pb, "Content-type: text/html\r\n");
    
        avio_printf(pb, "Pragma: no-cache\r\n");
        avio_printf(pb, "\r\n");
    
        avio_printf(pb, "<!DOCTYPE html>\n");
    
        avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
    
            avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n",
                        c->stream->feed_filename);
    
        avio_printf(pb, "</head>\n<body>");
        avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* format status */
    
        avio_printf(pb, "<h2>Available Streams</h2>\n");
    
        avio_printf(pb, "<table>\n");
        avio_printf(pb, "<tr><th>Path<th>Served<br>Conns<th><br>bytes<th>Format<th>Bit rate<br>kbit/s<th>Video<br>kbit/s<th><br>Codec<th>Audio<br>kbit/s<th><br>Codec<th>Feed\n");
    
        stream = config.first_stream;
    
        while (stream) {
    
            char sfilename[1024];
            char *eosf;
    
    
            if (stream->feed == stream) {
                stream = stream->next;
                continue;
            }
    
    
            av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
            eosf = sfilename + strlen(sfilename);
            if (eosf - sfilename >= 4) {
                if (strcmp(eosf - 4, ".asf") == 0)
                    strcpy(eosf - 4, ".asx");