Mercurial > mplayer.hg
annotate stream/http.c @ 35372:9d626905f38b
Add a version script to hide all symbols.
This should give better startup performance at least
and reduces the stripped binary size by about 500 kB for me.
However it means that stripped binaries will give only
completely useless backtraces since the compiler
will no be able to find the name of even a single function.
author | reimar |
---|---|
date | Fri, 23 Nov 2012 20:53:54 +0000 |
parents | aa2026fd0c76 |
children | 6065fb95813e |
rev | line source |
---|---|
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
1 /* |
902 | 2 * HTTP Helper |
30426
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
3 * |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
4 * Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com> |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
5 * |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
6 * This file is part of MPlayer. |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
7 * |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
8 * MPlayer is free software; you can redistribute it and/or modify |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
9 * it under the terms of the GNU General Public License as published by |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
10 * the Free Software Foundation; either version 2 of the License, or |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
11 * (at your option) any later version. |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
12 * |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
13 * MPlayer is distributed in the hope that it will be useful, |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
16 * GNU General Public License for more details. |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
17 * |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
18 * You should have received a copy of the GNU General Public License along |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
19 * with MPlayer; if not, write to the Free Software Foundation, Inc., |
ce0122361a39
Add license header to all files missing it in the stream subdirectory.
diego
parents:
30357
diff
changeset
|
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
902 | 21 */ |
22 | |
15614
a4a46131ee71
Change header order to avoid compile error because of STREAM_SEEK
reimar
parents:
15585
diff
changeset
|
23 #include "config.h" |
a4a46131ee71
Change header order to avoid compile error because of STREAM_SEEK
reimar
parents:
15585
diff
changeset
|
24 |
870 | 25 #include <stdio.h> |
26 #include <stdlib.h> | |
27 #include <string.h> | |
15585 | 28 #include <unistd.h> |
870 | 29 |
28402 | 30 #if !HAVE_WINSOCK2_H |
27472
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
31 #else |
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
32 #include <winsock2.h> |
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
33 #include <ws2tcpip.h> |
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
34 #endif |
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
35 |
c0b233cd30ca
Revert moving closesocket definition and network headers to network.h.
diego
parents:
27464
diff
changeset
|
36 #include "http.h" |
4816
f1dea39a50bb
Fixed the http response parser when the http header only has the HTTP
bertrand
parents:
4311
diff
changeset
|
37 #include "url.h" |
5915 | 38 #include "mp_msg.h" |
870 | 39 |
15585 | 40 #include "stream.h" |
19312
ab8d6b6deb63
proper inclusion of demuxer.h (including libmpdemux in Makefile only was to make previous split easier)
ben
parents:
19271
diff
changeset
|
41 #include "libmpdemux/demuxer.h" |
15585 | 42 #include "network.h" |
43 #include "help_mp.h" | |
44 | |
32585 | 45 #include "libavutil/base64.h" |
15585 | 46 |
16013 | 47 typedef struct { |
48 unsigned metaint; | |
49 unsigned metapos; | |
50 int is_ultravox; | |
51 } scast_data_t; | |
52 | |
53 /** | |
54 * \brief first read any data from sc->buffer then from fd | |
55 * \param fd file descriptor to read data from | |
56 * \param buffer buffer to read into | |
57 * \param len how many bytes to read | |
58 * \param sc streaming control containing buffer to read from first | |
59 * \return len unless there is a read error or eof | |
60 */ | |
61 static unsigned my_read(int fd, char *buffer, int len, streaming_ctrl_t *sc) { | |
62 unsigned pos = 0; | |
63 unsigned cp_len = sc->buffer_size - sc->buffer_pos; | |
64 if (cp_len > len) | |
65 cp_len = len; | |
66 memcpy(buffer, &sc->buffer[sc->buffer_pos], cp_len); | |
67 sc->buffer_pos += cp_len; | |
68 pos += cp_len; | |
69 while (pos < len) { | |
16070 | 70 int ret = recv(fd, &buffer[pos], len - pos, 0); |
16013 | 71 if (ret <= 0) |
72 break; | |
73 pos += ret; | |
74 } | |
75 return pos; | |
76 } | |
77 | |
16032 | 78 /** |
79 * \brief read and process (i.e. discard *g*) a block of ultravox metadata | |
80 * \param fd file descriptor to read from | |
81 * \param sc streaming_ctrl_t whose buffer is consumed before reading from fd | |
82 * \return number of real data before next metadata block starts or 0 on error | |
34343 | 83 * |
84 * You can use unsv://samples.mplayerhq.hu/V-codecs/VP5/vp5_artefacts.nsv to | |
85 * test. | |
16032 | 86 */ |
16013 | 87 static unsigned uvox_meta_read(int fd, streaming_ctrl_t *sc) { |
88 unsigned metaint; | |
16070 | 89 unsigned char info[6] = {0, 0, 0, 0, 0, 0}; |
90 int info_read; | |
16013 | 91 do { |
16070 | 92 info_read = my_read(fd, info, 1, sc); |
16013 | 93 if (info[0] == 0x00) |
16070 | 94 info_read = my_read(fd, info, 6, sc); |
16013 | 95 else |
16070 | 96 info_read += my_read(fd, &info[1], 5, sc); |
97 if (info_read != 6) // read error or eof | |
98 return 0; | |
16031
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
99 // sync byte and reserved flags |
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
100 if (info[0] != 0x5a || (info[1] & 0xfc) != 0x00) { |
16013 | 101 mp_msg(MSGT_DEMUXER, MSGL_ERR, "Invalid or unknown uvox metadata\n"); |
102 return 0; | |
103 } | |
16031
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
104 if (info[1] & 0x01) |
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
105 mp_msg(MSGT_DEMUXER, MSGL_WARN, "Encrypted ultravox data\n"); |
16013 | 106 metaint = info[4] << 8 | info[5]; |
16031
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
107 if ((info[3] & 0xf) < 0x07) { // discard any metadata nonsense |
16013 | 108 char *metabuf = malloc(metaint); |
109 my_read(fd, metabuf, metaint, sc); | |
110 free(metabuf); | |
111 } | |
16031
c2e78215f0d9
Ultravox improvements according to specs (didn't know they existed *g*)
reimar
parents:
16013
diff
changeset
|
112 } while ((info[3] & 0xf) < 0x07); |
16013 | 113 return metaint; |
114 } | |
115 | |
116 /** | |
117 * \brief read one scast meta data entry and print it | |
16032 | 118 * \param fd file descriptor to read from |
119 * \param sc streaming_ctrl_t whose buffer is consumed before reading from fd | |
16013 | 120 */ |
121 static void scast_meta_read(int fd, streaming_ctrl_t *sc) { | |
122 unsigned char tmp = 0; | |
123 unsigned metalen; | |
124 my_read(fd, &tmp, 1, sc); | |
125 metalen = tmp * 16; | |
126 if (metalen > 0) { | |
30938 | 127 int i; |
30941
fb3bde3ec3a8
Change type to uint8_t to avoid checks depending on char signedness.
reimar
parents:
30938
diff
changeset
|
128 uint8_t *info = malloc(metalen + 1); |
16013 | 129 unsigned nlen = my_read(fd, info, metalen, sc); |
30938 | 130 // avoid breaking the user's terminal too much |
131 if (nlen > 256) nlen = 256; | |
132 for (i = 0; i < nlen; i++) | |
133 if (info[i] && info[i] < 32) info[i] = '?'; | |
16013 | 134 info[nlen] = 0; |
135 mp_msg(MSGT_DEMUXER, MSGL_INFO, "\nICY Info: %s\n", info); | |
136 free(info); | |
137 } | |
138 } | |
139 | |
16032 | 140 /** |
141 * \brief read data from scast/ultravox stream without any metadata | |
142 * \param fd file descriptor to read from | |
143 * \param buffer buffer to read data into | |
144 * \param size number of bytes to read | |
145 * \param sc streaming_ctrl_t whose buffer is consumed before reading from fd | |
146 */ | |
16013 | 147 static int scast_streaming_read(int fd, char *buffer, int size, |
148 streaming_ctrl_t *sc) { | |
149 scast_data_t *sd = (scast_data_t *)sc->data; | |
150 unsigned block, ret; | |
151 unsigned done = 0; | |
152 | |
153 // first read remaining data up to next metadata | |
154 block = sd->metaint - sd->metapos; | |
155 if (block > size) | |
156 block = size; | |
157 ret = my_read(fd, buffer, block, sc); | |
158 sd->metapos += ret; | |
159 done += ret; | |
160 if (ret != block) // read problems or eof | |
161 size = done; | |
162 | |
163 while (done < size) { // now comes the metadata | |
164 if (sd->is_ultravox) | |
16070 | 165 { |
16013 | 166 sd->metaint = uvox_meta_read(fd, sc); |
16070 | 167 if (!sd->metaint) |
168 size = done; | |
169 } | |
16013 | 170 else |
171 scast_meta_read(fd, sc); // read and display metadata | |
172 sd->metapos = 0; | |
173 block = size - done; | |
174 if (block > sd->metaint) | |
175 block = sd->metaint; | |
176 ret = my_read(fd, &buffer[done], block, sc); | |
177 sd->metapos += ret; | |
178 done += ret; | |
179 if (ret != block) // read problems or eof | |
180 size = done; | |
181 } | |
182 return done; | |
183 } | |
184 | |
185 static int scast_streaming_start(stream_t *stream) { | |
186 int metaint; | |
187 scast_data_t *scast_data; | |
188 HTTP_header_t *http_hdr = stream->streaming_ctrl->data; | |
16070 | 189 int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0; |
16013 | 190 if (!stream || stream->fd < 0 || !http_hdr) |
191 return -1; | |
192 if (is_ultravox) | |
193 metaint = 0; | |
194 else { | |
195 metaint = atoi(http_get_field(http_hdr, "Icy-MetaInt")); | |
196 if (metaint <= 0) | |
197 return -1; | |
198 } | |
199 stream->streaming_ctrl->buffer = malloc(http_hdr->body_size); | |
200 stream->streaming_ctrl->buffer_size = http_hdr->body_size; | |
201 stream->streaming_ctrl->buffer_pos = 0; | |
202 memcpy(stream->streaming_ctrl->buffer, http_hdr->body, http_hdr->body_size); | |
203 scast_data = malloc(sizeof(scast_data_t)); | |
204 scast_data->metaint = metaint; | |
205 scast_data->metapos = 0; | |
206 scast_data->is_ultravox = is_ultravox; | |
207 http_free(http_hdr); | |
208 stream->streaming_ctrl->data = scast_data; | |
209 stream->streaming_ctrl->streaming_read = scast_streaming_read; | |
210 stream->streaming_ctrl->streaming_seek = NULL; | |
211 stream->streaming_ctrl->prebuffer_size = 64 * 1024; // 64 KBytes | |
212 stream->streaming_ctrl->buffering = 1; | |
213 stream->streaming_ctrl->status = streaming_playing_e; | |
214 return 0; | |
215 } | |
216 | |
15585 | 217 static int nop_streaming_start( stream_t *stream ) { |
218 HTTP_header_t *http_hdr = NULL; | |
219 char *next_url=NULL; | |
220 int fd,ret; | |
221 if( stream==NULL ) return -1; | |
222 | |
223 fd = stream->fd; | |
224 if( fd<0 ) { | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
225 fd = http_send_request( stream->streaming_ctrl->url, 0 ); |
15585 | 226 if( fd<0 ) return -1; |
227 http_hdr = http_read_response( fd ); | |
228 if( http_hdr==NULL ) return -1; | |
229 | |
230 switch( http_hdr->status_code ) { | |
231 case 200: // OK | |
232 mp_msg(MSGT_NETWORK,MSGL_V,"Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") ); | |
233 mp_msg(MSGT_NETWORK,MSGL_V,"Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); | |
234 if( http_hdr->body_size>0 ) { | |
235 if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) { | |
236 http_free( http_hdr ); | |
237 return -1; | |
238 } | |
239 } | |
240 break; | |
241 // Redirect | |
242 case 301: // Permanently | |
243 case 302: // Temporarily | |
19459
6dad4b1efb82
Handle 303 (See Other) redirect, part of a patch by Benjamin Zores (ben at geexbox org)
reimar
parents:
19312
diff
changeset
|
244 case 303: // See Other |
32630
2653cb380532
Add support for HTTP 307 (Temporary Redirect) responses.
cboesch
parents:
32585
diff
changeset
|
245 case 307: // Temporarily (since HTTP/1.1) |
15585 | 246 ret=-1; |
247 next_url = http_get_field( http_hdr, "Location" ); | |
248 | |
35301
8f6d3f8ffa61
Add url_new_with_proxy function to reduce code duplication and memleaks.
reimar
parents:
35229
diff
changeset
|
249 if (next_url != NULL) { |
15585 | 250 mp_msg(MSGT_NETWORK,MSGL_STATUS,"Redirected: Using this url instead %s\n",next_url); |
35301
8f6d3f8ffa61
Add url_new_with_proxy function to reduce code duplication and memleaks.
reimar
parents:
35229
diff
changeset
|
251 stream->streaming_ctrl->url=url_new_with_proxy(next_url); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
252 ret=nop_streaming_start(stream); //recursively get streaming started |
15585 | 253 } else { |
254 mp_msg(MSGT_NETWORK,MSGL_ERR,"Redirection failed\n"); | |
255 closesocket( fd ); | |
256 fd = -1; | |
257 } | |
258 return ret; | |
259 break; | |
260 case 401: //Authorization required | |
261 case 403: //Forbidden | |
262 case 404: //Not found | |
263 case 500: //Server Error | |
264 default: | |
265 mp_msg(MSGT_NETWORK,MSGL_ERR,"Server returned code %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); | |
266 closesocket( fd ); | |
267 fd = -1; | |
268 return -1; | |
269 break; | |
270 } | |
271 stream->fd = fd; | |
272 } else { | |
273 http_hdr = (HTTP_header_t*)stream->streaming_ctrl->data; | |
274 if( http_hdr->body_size>0 ) { | |
275 if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) { | |
276 http_free( http_hdr ); | |
277 stream->streaming_ctrl->data = NULL; | |
278 return -1; | |
279 } | |
280 } | |
281 } | |
282 | |
283 if( http_hdr ) { | |
284 http_free( http_hdr ); | |
285 stream->streaming_ctrl->data = NULL; | |
286 } | |
287 | |
288 stream->streaming_ctrl->streaming_read = nop_streaming_read; | |
289 stream->streaming_ctrl->streaming_seek = nop_streaming_seek; | |
290 stream->streaming_ctrl->prebuffer_size = 64*1024; // 64 KBytes | |
291 stream->streaming_ctrl->buffering = 1; | |
292 stream->streaming_ctrl->status = streaming_playing_e; | |
293 return 0; | |
294 } | |
295 | |
870 | 296 HTTP_header_t * |
17566
f580a7755ac5
Patch by Stefan Huehner / stefan % huehner ! org \
rathann
parents:
16948
diff
changeset
|
297 http_new_header(void) { |
870 | 298 HTTP_header_t *http_hdr; |
299 | |
32636 | 300 http_hdr = calloc(1, sizeof(*http_hdr)); |
870 | 301 if( http_hdr==NULL ) return NULL; |
302 | |
303 return http_hdr; | |
304 } | |
305 | |
306 void | |
307 http_free( HTTP_header_t *http_hdr ) { | |
3039 | 308 HTTP_field_t *field, *field2free; |
870 | 309 if( http_hdr==NULL ) return; |
32511
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
310 free(http_hdr->protocol); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
311 free(http_hdr->uri); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
312 free(http_hdr->reason_phrase); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
313 free(http_hdr->field_search); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
314 free(http_hdr->method); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
315 free(http_hdr->buffer); |
3039 | 316 field = http_hdr->first_field; |
317 while( field!=NULL ) { | |
318 field2free = field; | |
32511
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
319 free(field->field_name); |
3039 | 320 field = field->next; |
321 free( field2free ); | |
322 } | |
870 | 323 free( http_hdr ); |
3039 | 324 http_hdr = NULL; |
870 | 325 } |
326 | |
902 | 327 int |
328 http_response_append( HTTP_header_t *http_hdr, char *response, int length ) { | |
1027 | 329 if( http_hdr==NULL || response==NULL || length<0 ) return -1; |
7304
7da2c2a68547
Check if realloc failed on http_hdr->buffer instead of ptr in http_response_append,
bertrand
parents:
7293
diff
changeset
|
330 |
18558
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
331 if( (unsigned)length > SIZE_MAX - http_hdr->buffer_size - 1) { |
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
332 mp_msg(MSGT_NETWORK,MSGL_FATAL,"Bad size in memory (re)allocation\n"); |
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
333 return -1; |
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
334 } |
30702 | 335 http_hdr->buffer = realloc( http_hdr->buffer, http_hdr->buffer_size+length+1 ); |
7304
7da2c2a68547
Check if realloc failed on http_hdr->buffer instead of ptr in http_response_append,
bertrand
parents:
7293
diff
changeset
|
336 if( http_hdr->buffer==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
337 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
902 | 338 return -1; |
339 } | |
7293 | 340 memcpy( http_hdr->buffer+http_hdr->buffer_size, response, length ); |
341 http_hdr->buffer_size += length; | |
342 http_hdr->buffer[http_hdr->buffer_size]=0; // close the string! | |
902 | 343 return http_hdr->buffer_size; |
344 } | |
345 | |
346 int | |
2489
0ecc1b4f7cf8
Added ASF http server streaming (Not mms streaming).
bertrand
parents:
2310
diff
changeset
|
347 http_is_header_entire( HTTP_header_t *http_hdr ) { |
902 | 348 if( http_hdr==NULL ) return -1; |
7293 | 349 if( http_hdr->buffer==NULL ) return 0; // empty |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
350 |
3784 | 351 if( strstr(http_hdr->buffer, "\r\n\r\n")==NULL && |
352 strstr(http_hdr->buffer, "\n\n")==NULL ) return 0; | |
353 return 1; | |
902 | 354 } |
355 | |
356 int | |
357 http_response_parse( HTTP_header_t *http_hdr ) { | |
870 | 358 char *hdr_ptr, *ptr; |
359 char *field=NULL; | |
18558
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
360 int pos_hdr_sep, hdr_sep_len; |
4928dd61f136
Fix potential integer overflows in memory allocation.
rtogni
parents:
18094
diff
changeset
|
361 size_t len; |
902 | 362 if( http_hdr==NULL ) return -1; |
363 if( http_hdr->is_parsed ) return 0; | |
870 | 364 |
365 // Get the protocol | |
902 | 366 hdr_ptr = strstr( http_hdr->buffer, " " ); |
870 | 367 if( hdr_ptr==NULL ) { |
5915 | 368 mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. No space separator found.\n"); |
902 | 369 return -1; |
870 | 370 } |
902 | 371 len = hdr_ptr-http_hdr->buffer; |
18879 | 372 http_hdr->protocol = malloc(len+1); |
870 | 373 if( http_hdr->protocol==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
374 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
902 | 375 return -1; |
870 | 376 } |
902 | 377 strncpy( http_hdr->protocol, http_hdr->buffer, len ); |
378 http_hdr->protocol[len]='\0'; | |
870 | 379 if( !strncasecmp( http_hdr->protocol, "HTTP", 4) ) { |
380 if( sscanf( http_hdr->protocol+5,"1.%d", &(http_hdr->http_minor_version) )!=1 ) { | |
5915 | 381 mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get HTTP minor version.\n"); |
902 | 382 return -1; |
870 | 383 } |
384 } | |
385 | |
386 // Get the status code | |
387 if( sscanf( ++hdr_ptr, "%d", &(http_hdr->status_code) )!=1 ) { | |
5915 | 388 mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get status code.\n"); |
902 | 389 return -1; |
870 | 390 } |
391 hdr_ptr += 4; | |
392 | |
393 // Get the reason phrase | |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
394 ptr = strstr( hdr_ptr, "\n" ); |
35229 | 395 if( ptr==NULL ) { |
5915 | 396 mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get the reason phrase.\n"); |
902 | 397 return -1; |
870 | 398 } |
399 len = ptr-hdr_ptr; | |
18879 | 400 http_hdr->reason_phrase = malloc(len+1); |
870 | 401 if( http_hdr->reason_phrase==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
402 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
902 | 403 return -1; |
870 | 404 } |
405 strncpy( http_hdr->reason_phrase, hdr_ptr, len ); | |
4311 | 406 if( http_hdr->reason_phrase[len-1]=='\r' ) { |
407 len--; | |
408 } | |
870 | 409 http_hdr->reason_phrase[len]='\0'; |
410 | |
411 // Set the position of the header separator: \r\n\r\n | |
8179
63a5e03f4346
Removed hard coded value for the length of the header separator.
bertrand
parents:
7304
diff
changeset
|
412 hdr_sep_len = 4; |
902 | 413 ptr = strstr( http_hdr->buffer, "\r\n\r\n" ); |
870 | 414 if( ptr==NULL ) { |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
415 ptr = strstr( http_hdr->buffer, "\n\n" ); |
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
416 if( ptr==NULL ) { |
5915 | 417 mp_msg(MSGT_NETWORK,MSGL_ERR,"Header may be incomplete. No CRLF CRLF found.\n"); |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
418 return -1; |
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
419 } |
8179
63a5e03f4346
Removed hard coded value for the length of the header separator.
bertrand
parents:
7304
diff
changeset
|
420 hdr_sep_len = 2; |
870 | 421 } |
902 | 422 pos_hdr_sep = ptr-http_hdr->buffer; |
870 | 423 |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
424 // Point to the first line after the method line. |
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
425 hdr_ptr = strstr( http_hdr->buffer, "\n" )+1; |
870 | 426 do { |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
427 ptr = hdr_ptr; |
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
428 while( *ptr!='\r' && *ptr!='\n' ) ptr++; |
870 | 429 len = ptr-hdr_ptr; |
4816
f1dea39a50bb
Fixed the http response parser when the http header only has the HTTP
bertrand
parents:
4311
diff
changeset
|
430 if( len==0 ) break; |
30702 | 431 field = realloc(field, len+1); |
870 | 432 if( field==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
433 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MemAllocFailed); |
902 | 434 return -1; |
870 | 435 } |
436 strncpy( field, hdr_ptr, len ); | |
437 field[len]='\0'; | |
438 http_set_field( http_hdr, field ); | |
3514
43518985def8
Handle broken server that doesn't send CRLF but jusr LF.
bertrand
parents:
3497
diff
changeset
|
439 hdr_ptr = ptr+((*ptr=='\r')?2:1); |
902 | 440 } while( hdr_ptr<(http_hdr->buffer+pos_hdr_sep) ); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
441 |
32511
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32052
diff
changeset
|
442 free(field); |
870 | 443 |
8179
63a5e03f4346
Removed hard coded value for the length of the header separator.
bertrand
parents:
7304
diff
changeset
|
444 if( pos_hdr_sep+hdr_sep_len<http_hdr->buffer_size ) { |
870 | 445 // Response has data! |
8179
63a5e03f4346
Removed hard coded value for the length of the header separator.
bertrand
parents:
7304
diff
changeset
|
446 http_hdr->body = http_hdr->buffer+pos_hdr_sep+hdr_sep_len; |
63a5e03f4346
Removed hard coded value for the length of the header separator.
bertrand
parents:
7304
diff
changeset
|
447 http_hdr->body_size = http_hdr->buffer_size-(pos_hdr_sep+hdr_sep_len); |
870 | 448 } |
449 | |
902 | 450 http_hdr->is_parsed = 1; |
451 return 0; | |
870 | 452 } |
453 | |
454 char * | |
902 | 455 http_build_request( HTTP_header_t *http_hdr ) { |
35317 | 456 char *ptr; |
902 | 457 int len; |
3039 | 458 HTTP_field_t *field; |
870 | 459 if( http_hdr==NULL ) return NULL; |
460 | |
461 if( http_hdr->method==NULL ) http_set_method( http_hdr, "GET"); | |
462 if( http_hdr->uri==NULL ) http_set_uri( http_hdr, "/"); | |
35317 | 463 if( !http_hdr->uri || !http_hdr->method) |
464 return NULL; | |
870 | 465 |
3497 | 466 //**** Compute the request length |
467 // Add the Method line | |
35317 | 468 len = strlen(http_hdr->method)+strlen(http_hdr->uri)+12; |
3497 | 469 // Add the fields |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
470 field = http_hdr->first_field; |
3039 | 471 while( field!=NULL ) { |
472 len += strlen(field->field_name)+2; | |
473 field = field->next; | |
474 } | |
3497 | 475 // Add the CRLF |
476 len += 2; | |
477 // Add the body | |
902 | 478 if( http_hdr->body!=NULL ) { |
479 len += http_hdr->body_size; | |
480 } | |
3497 | 481 // Free the buffer if it was previously used |
902 | 482 if( http_hdr->buffer!=NULL ) { |
483 free( http_hdr->buffer ); | |
484 http_hdr->buffer = NULL; | |
485 } | |
18879 | 486 http_hdr->buffer = malloc(len+1); |
902 | 487 if( http_hdr->buffer==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
488 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MemAllocFailed); |
902 | 489 return NULL; |
490 } | |
491 http_hdr->buffer_size = len; | |
492 | |
3497 | 493 //*** Building the request |
902 | 494 ptr = http_hdr->buffer; |
3497 | 495 // Add the method line |
35317 | 496 ptr += sprintf( ptr, "%s %s HTTP/1.%d\r\n", http_hdr->method, http_hdr->uri, http_hdr->http_minor_version ); |
3039 | 497 field = http_hdr->first_field; |
3497 | 498 // Add the field |
3039 | 499 while( field!=NULL ) { |
500 ptr += sprintf( ptr, "%s\r\n", field->field_name ); | |
501 field = field->next; | |
502 } | |
870 | 503 ptr += sprintf( ptr, "\r\n" ); |
3497 | 504 // Add the body |
870 | 505 if( http_hdr->body!=NULL ) { |
506 memcpy( ptr, http_hdr->body, http_hdr->body_size ); | |
507 } | |
3497 | 508 |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
509 return http_hdr->buffer; |
870 | 510 } |
511 | |
512 char * | |
513 http_get_field( HTTP_header_t *http_hdr, const char *field_name ) { | |
514 if( http_hdr==NULL || field_name==NULL ) return NULL; | |
3039 | 515 http_hdr->field_search_pos = http_hdr->first_field; |
30702 | 516 http_hdr->field_search = realloc( http_hdr->field_search, strlen(field_name)+1 ); |
870 | 517 if( http_hdr->field_search==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
518 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
870 | 519 return NULL; |
520 } | |
521 strcpy( http_hdr->field_search, field_name ); | |
522 return http_get_next_field( http_hdr ); | |
523 } | |
524 | |
525 char * | |
526 http_get_next_field( HTTP_header_t *http_hdr ) { | |
527 char *ptr; | |
3039 | 528 HTTP_field_t *field; |
870 | 529 if( http_hdr==NULL ) return NULL; |
530 | |
3039 | 531 field = http_hdr->field_search_pos; |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
532 while( field!=NULL ) { |
3039 | 533 ptr = strstr( field->field_name, ":" ); |
870 | 534 if( ptr==NULL ) return NULL; |
3039 | 535 if( !strncasecmp( field->field_name, http_hdr->field_search, ptr-(field->field_name) ) ) { |
870 | 536 ptr++; // Skip the column |
537 while( ptr[0]==' ' ) ptr++; // Skip the spaces if there is some | |
3039 | 538 http_hdr->field_search_pos = field->next; |
870 | 539 return ptr; // return the value without the field name |
540 } | |
3039 | 541 field = field->next; |
870 | 542 } |
543 return NULL; | |
544 } | |
545 | |
546 void | |
3039 | 547 http_set_field( HTTP_header_t *http_hdr, const char *field_name ) { |
548 HTTP_field_t *new_field; | |
549 if( http_hdr==NULL || field_name==NULL ) return; | |
870 | 550 |
18879 | 551 new_field = malloc(sizeof(HTTP_field_t)); |
3039 | 552 if( new_field==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
553 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
870 | 554 return; |
555 } | |
3039 | 556 new_field->next = NULL; |
18879 | 557 new_field->field_name = malloc(strlen(field_name)+1); |
3039 | 558 if( new_field->field_name==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
559 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
27834
d35bcab9833b
Avoid a memleak if allocation of field_name fails, fixes bug #1319.
reimar
parents:
27473
diff
changeset
|
560 free(new_field); |
3039 | 561 return; |
562 } | |
563 strcpy( new_field->field_name, field_name ); | |
564 | |
565 if( http_hdr->last_field==NULL ) { | |
566 http_hdr->first_field = new_field; | |
567 } else { | |
568 http_hdr->last_field->next = new_field; | |
569 } | |
570 http_hdr->last_field = new_field; | |
870 | 571 http_hdr->field_nb++; |
572 } | |
573 | |
574 void | |
575 http_set_method( HTTP_header_t *http_hdr, const char *method ) { | |
576 if( http_hdr==NULL || method==NULL ) return; | |
577 | |
18879 | 578 http_hdr->method = malloc(strlen(method)+1); |
870 | 579 if( http_hdr->method==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
580 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
870 | 581 return; |
582 } | |
583 strcpy( http_hdr->method, method ); | |
584 } | |
585 | |
586 void | |
587 http_set_uri( HTTP_header_t *http_hdr, const char *uri ) { | |
588 if( http_hdr==NULL || uri==NULL ) return; | |
589 | |
18879 | 590 http_hdr->uri = malloc(strlen(uri)+1); |
870 | 591 if( http_hdr->uri==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
592 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
870 | 593 return; |
594 } | |
595 strcpy( http_hdr->uri, uri ); | |
596 } | |
597 | |
32547
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
598 static int |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
599 http_add_authentication( HTTP_header_t *http_hdr, const char *username, const char *password, const char *auth_str ) { |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
600 char *auth = NULL, *usr_pass = NULL, *b64_usr_pass = NULL; |
32585 | 601 int encoded_len, pass_len=0; |
602 size_t auth_len, usr_pass_len; | |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
603 int res = -1; |
6514 | 604 if( http_hdr==NULL || username==NULL ) return -1; |
605 | |
606 if( password!=NULL ) { | |
607 pass_len = strlen(password); | |
608 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
609 |
32585 | 610 usr_pass_len = strlen(username) + 1 + pass_len; |
611 usr_pass = malloc(usr_pass_len + 1); | |
6514 | 612 if( usr_pass==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
613 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
614 goto out; |
6514 | 615 } |
616 | |
617 sprintf( usr_pass, "%s:%s", username, (password==NULL)?"":password ); | |
618 | |
32585 | 619 encoded_len = AV_BASE64_SIZE(usr_pass_len); |
18879 | 620 b64_usr_pass = malloc(encoded_len); |
6514 | 621 if( b64_usr_pass==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
622 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
623 goto out; |
6514 | 624 } |
32585 | 625 av_base64_encode(b64_usr_pass, encoded_len, usr_pass, usr_pass_len); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
626 |
32547
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
627 auth_len = encoded_len + 100; |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
628 auth = malloc(auth_len); |
6514 | 629 if( auth==NULL ) { |
32515
84bdbf9e9c48
Use MSGTR_MemAllocFailed instead of hardcoded string
cboesch
parents:
32511
diff
changeset
|
630 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed); |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
631 goto out; |
6514 | 632 } |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
633 |
32547
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
634 snprintf(auth, auth_len, "%s: Basic %s", auth_str, b64_usr_pass); |
6514 | 635 http_set_field( http_hdr, auth ); |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
636 res = 0; |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
637 |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
638 out: |
6514 | 639 free( usr_pass ); |
640 free( b64_usr_pass ); | |
641 free( auth ); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
642 |
18094
16f2bcd5d148
free memory on error in http_add_basic_authentication
reimar
parents:
17932
diff
changeset
|
643 return res; |
6514 | 644 } |
645 | |
32547
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
646 int |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
647 http_add_basic_authentication( HTTP_header_t *http_hdr, const char *username, const char *password ) { |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
648 return http_add_authentication(http_hdr, username, password, "Authorization"); |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
649 } |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
650 |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
651 int |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
652 http_add_basic_proxy_authentication( HTTP_header_t *http_hdr, const char *username, const char *password ) { |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
653 return http_add_authentication(http_hdr, username, password, "Proxy-Authorization"); |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
654 } |
6a629e6fdb09
Add Proxy-Authorization header to authenticate on proxies
cboesch
parents:
32515
diff
changeset
|
655 |
870 | 656 void |
657 http_debug_hdr( HTTP_header_t *http_hdr ) { | |
3039 | 658 HTTP_field_t *field; |
659 int i = 0; | |
902 | 660 if( http_hdr==NULL ) return; |
870 | 661 |
5915 | 662 mp_msg(MSGT_NETWORK,MSGL_V,"--- HTTP DEBUG HEADER --- START ---\n"); |
663 mp_msg(MSGT_NETWORK,MSGL_V,"protocol: [%s]\n", http_hdr->protocol ); | |
664 mp_msg(MSGT_NETWORK,MSGL_V,"http minor version: [%d]\n", http_hdr->http_minor_version ); | |
665 mp_msg(MSGT_NETWORK,MSGL_V,"uri: [%s]\n", http_hdr->uri ); | |
666 mp_msg(MSGT_NETWORK,MSGL_V,"method: [%s]\n", http_hdr->method ); | |
667 mp_msg(MSGT_NETWORK,MSGL_V,"status code: [%d]\n", http_hdr->status_code ); | |
668 mp_msg(MSGT_NETWORK,MSGL_V,"reason phrase: [%s]\n", http_hdr->reason_phrase ); | |
33841
25667edae85c
cleanup: adjust printf format strings to match parameter types
diego
parents:
32720
diff
changeset
|
669 mp_msg(MSGT_NETWORK,MSGL_V,"body size: [%zu]\n", http_hdr->body_size ); |
870 | 670 |
5915 | 671 mp_msg(MSGT_NETWORK,MSGL_V,"Fields:\n"); |
3039 | 672 field = http_hdr->first_field; |
673 while( field!=NULL ) { | |
5915 | 674 mp_msg(MSGT_NETWORK,MSGL_V," %d - %s\n", i++, field->field_name ); |
3039 | 675 field = field->next; |
676 } | |
5915 | 677 mp_msg(MSGT_NETWORK,MSGL_V,"--- HTTP DEBUG HEADER --- END ---\n"); |
870 | 678 } |
6514 | 679 |
25970
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
680 static void print_icy_metadata(HTTP_header_t *http_hdr) { |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
681 const char *field_data; |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
682 // note: I skip icy-notice1 and 2, as they contain html <BR> |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
683 // and are IMHO useless info ::atmos |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
684 if( (field_data = http_get_field(http_hdr, "icy-name")) != NULL ) |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
685 mp_msg(MSGT_NETWORK,MSGL_INFO,"Name : %s\n", field_data); |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
686 if( (field_data = http_get_field(http_hdr, "icy-genre")) != NULL ) |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
687 mp_msg(MSGT_NETWORK,MSGL_INFO,"Genre : %s\n", field_data); |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
688 if( (field_data = http_get_field(http_hdr, "icy-url")) != NULL ) |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
689 mp_msg(MSGT_NETWORK,MSGL_INFO,"Website: %s\n", field_data); |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
690 // XXX: does this really mean public server? ::atmos |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
691 if( (field_data = http_get_field(http_hdr, "icy-pub")) != NULL ) |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
692 mp_msg(MSGT_NETWORK,MSGL_INFO,"Public : %s\n", atoi(field_data)?"yes":"no"); |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
693 if( (field_data = http_get_field(http_hdr, "icy-br")) != NULL ) |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
694 mp_msg(MSGT_NETWORK,MSGL_INFO,"Bitrate: %skbit/s\n", field_data); |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
695 } |
c6ec51cc3b13
Move printing of Icy-Metadata into an extra function
reimar
parents:
25969
diff
changeset
|
696 |
21567 | 697 //! If this function succeeds you must closesocket stream->fd |
15585 | 698 static int http_streaming_start(stream_t *stream, int* file_format) { |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
699 HTTP_header_t *http_hdr = NULL; |
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
700 int fd = stream->fd; |
25135
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
701 int res = STREAM_UNSUPPORTED; |
15585 | 702 int redirect = 0; |
703 int auth_retry=0; | |
704 int seekable=0; | |
705 char *content_type; | |
30357
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
706 const char *content_length; |
15585 | 707 char *next_url; |
708 URL_t *url = stream->streaming_ctrl->url; | |
6514 | 709 |
15585 | 710 do |
711 { | |
21541
3b4ed8857b38
Fix potential endless loop in http_streaming_start due
reimar
parents:
21540
diff
changeset
|
712 redirect = 0; |
35308
f570e9faa3c7
Fix off-by-one errors in file descriptor validity checks.
reimar
parents:
35301
diff
changeset
|
713 if (fd >= 0) closesocket(fd); |
15585 | 714 fd = http_send_request( url, 0 ); |
715 if( fd<0 ) { | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
716 goto err_out; |
15585 | 717 } |
718 | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
719 http_free(http_hdr); |
15585 | 720 http_hdr = http_read_response( fd ); |
721 if( http_hdr==NULL ) { | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
722 goto err_out; |
15585 | 723 } |
724 | |
17932 | 725 if( mp_msg_test(MSGT_NETWORK,MSGL_V) ) { |
15585 | 726 http_debug_hdr( http_hdr ); |
727 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
728 |
15585 | 729 // Check if we can make partial content requests and thus seek in http-streams |
730 if( http_hdr!=NULL && http_hdr->status_code==200 ) { | |
30106 | 731 const char *accept_ranges = http_get_field(http_hdr,"Accept-Ranges"); |
30105
31b7d4637b50
Fix crash if http reply contains neither "Accept-Ranges" nor "Server" fields.
reimar
parents:
30103
diff
changeset
|
732 const char *server = http_get_field(http_hdr, "Server"); |
30106 | 733 if (accept_ranges) |
15585 | 734 seekable = strncmp(accept_ranges,"bytes",5)==0; |
32720
1afbc8e3ff55
100l, fix hack to check for MakeMKV server being inverted.
reimar
parents:
32698
diff
changeset
|
735 else if (server && (strcmp(server, "gvs 1.0") == 0 || |
1afbc8e3ff55
100l, fix hack to check for MakeMKV server being inverted.
reimar
parents:
32698
diff
changeset
|
736 strncmp(server, "MakeMKV", 7) == 0)) { |
32698
b171c744cd06
Add MakeMKV to list of webservers always supporting ranges even when they
reimar
parents:
32636
diff
changeset
|
737 // HACK for youtube and MakeMKV incorrectly claiming not to support seeking |
b171c744cd06
Add MakeMKV to list of webservers always supporting ranges even when they
reimar
parents:
32636
diff
changeset
|
738 mp_msg(MSGT_NETWORK, MSGL_WARN, "Broken webserver, incorrectly claims to not support Accept-Ranges\n"); |
b171c744cd06
Add MakeMKV to list of webservers always supporting ranges even when they
reimar
parents:
32636
diff
changeset
|
739 seekable = 1; |
b171c744cd06
Add MakeMKV to list of webservers always supporting ranges even when they
reimar
parents:
32636
diff
changeset
|
740 } |
15585 | 741 } |
742 | |
25971
64b1e4ea04fc
Always display Icy-Metadata if available, whether we recognize an ICY-Server
reimar
parents:
25970
diff
changeset
|
743 print_icy_metadata(http_hdr); |
64b1e4ea04fc
Always display Icy-Metadata if available, whether we recognize an ICY-Server
reimar
parents:
25970
diff
changeset
|
744 |
15585 | 745 // Check if the response is an ICY status_code reason_phrase |
25968
c7f41f9e2eb8
Detect IceCast also by Icy-MetaInt header part in http_streaming_start(),
reimar
parents:
25246
diff
changeset
|
746 if( !strcasecmp(http_hdr->protocol, "ICY") || |
c7f41f9e2eb8
Detect IceCast also by Icy-MetaInt header part in http_streaming_start(),
reimar
parents:
25246
diff
changeset
|
747 http_get_field(http_hdr, "Icy-MetaInt") ) { |
15585 | 748 switch( http_hdr->status_code ) { |
749 case 200: { // OK | |
25969 | 750 char *field_data; |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
751 // If content-type == video/nsv we most likely have a winamp video stream |
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
752 // otherwise it should be mp3. if there are more types consider adding mime type |
15585 | 753 // handling like later |
754 if ( (field_data = http_get_field(http_hdr, "content-type")) != NULL && (!strcmp(field_data, "video/nsv") || !strcmp(field_data, "misc/ultravox"))) | |
755 *file_format = DEMUXER_TYPE_NSV; | |
16948
9b7925705f5b
Add another content-type for aac audio in shoutcast streams
rtognimp
parents:
16932
diff
changeset
|
756 else if ( (field_data = http_get_field(http_hdr, "content-type")) != NULL && (!strcmp(field_data, "audio/aacp") || !strcmp(field_data, "audio/aac"))) |
16917
c45409728a9d
Use correct demuxer type for aac in shoutcast streams
rtognimp
parents:
16614
diff
changeset
|
757 *file_format = DEMUXER_TYPE_AAC; |
15585 | 758 else |
759 *file_format = DEMUXER_TYPE_AUDIO; | |
25135
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
760 res = STREAM_ERROR; |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
761 goto out; |
15585 | 762 } |
763 case 400: // Server Full | |
764 mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server is full, skipping!\n"); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
765 goto err_out; |
15585 | 766 case 401: // Service Unavailable |
767 mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server return service unavailable, skipping!\n"); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
768 goto err_out; |
15585 | 769 case 403: // Service Forbidden |
770 mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server return 'Service Forbidden'\n"); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
771 goto err_out; |
15585 | 772 case 404: // Resource Not Found |
773 mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server couldn't find requested stream, skipping!\n"); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
774 goto err_out; |
15585 | 775 default: |
776 mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: unhandled ICY-Errorcode, contact MPlayer developers!\n"); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
777 goto err_out; |
15585 | 778 } |
779 } | |
780 | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
781 // Assume standard http if not ICY |
15585 | 782 switch( http_hdr->status_code ) { |
783 case 200: // OK | |
30357
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
784 content_length = http_get_field(http_hdr, "Content-Length"); |
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
785 if (content_length) { |
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
786 mp_msg(MSGT_NETWORK,MSGL_V,"Content-Length: [%s]\n", content_length); |
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
787 stream->end_pos = atoll(content_length); |
3fdf04500df2
Handle Content-Length also when Content-Type is not set.
reimar
parents:
30356
diff
changeset
|
788 } |
15585 | 789 // Look if we can use the Content-Type |
790 content_type = http_get_field( http_hdr, "Content-Type" ); | |
791 if( content_type!=NULL ) { | |
32557 | 792 unsigned int i; |
793 | |
15585 | 794 mp_msg(MSGT_NETWORK,MSGL_V,"Content-Type: [%s]\n", content_type ); |
795 // Check in the mime type table for a demuxer type | |
32557 | 796 for (i = 0; mime_type_table[i].mime_type != NULL; i++) { |
15585 | 797 if( !strcasecmp( content_type, mime_type_table[i].mime_type ) ) { |
798 *file_format = mime_type_table[i].demuxer_type; | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
799 res = seekable; |
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
800 goto out; |
15585 | 801 } |
802 } | |
803 } | |
804 // Not found in the mime type table, don't fail, | |
805 // we should try raw HTTP | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
806 res = seekable; |
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
807 goto out; |
15585 | 808 // Redirect |
809 case 301: // Permanently | |
810 case 302: // Temporarily | |
19459
6dad4b1efb82
Handle 303 (See Other) redirect, part of a patch by Benjamin Zores (ben at geexbox org)
reimar
parents:
19312
diff
changeset
|
811 case 303: // See Other |
32630
2653cb380532
Add support for HTTP 307 (Temporary Redirect) responses.
cboesch
parents:
32585
diff
changeset
|
812 case 307: // Temporarily (since HTTP/1.1) |
15585 | 813 // TODO: RFC 2616, recommand to detect infinite redirection loops |
814 next_url = http_get_field( http_hdr, "Location" ); | |
815 if( next_url!=NULL ) { | |
25238
f42b8e689416
Preserve unsv:// protocol specifier over http redirects.
reimar
parents:
25211
diff
changeset
|
816 int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0; |
20784
720206eef78b
Support URL redirections that do not specify full URL.
reimar
parents:
20185
diff
changeset
|
817 stream->streaming_ctrl->url = url_redirect( &url, next_url ); |
35113 | 818 if (url_is_protocol(url, "mms")) { |
25135
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
819 res = STREAM_REDIRECTED; |
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
820 goto err_out; |
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
821 } |
35113 | 822 if (!url_is_protocol(url, "http")) { |
25135
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
823 mp_msg(MSGT_NETWORK,MSGL_ERR,"Unsupported http %d redirect to %s protocol\n", http_hdr->status_code, url->protocol); |
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
824 goto err_out; |
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
825 } |
35113 | 826 if (is_ultravox) |
827 url_set_protocol(url, "unsv"); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
828 redirect = 1; |
15585 | 829 } |
830 break; | |
831 case 401: // Authentication required | |
21566
79d969f9eec0
STREAM_UNSUPPORTED is -1, so use the former for return value in all places.
reimar
parents:
21565
diff
changeset
|
832 if( http_authenticate(http_hdr, url, &auth_retry)<0 ) |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
833 goto err_out; |
15585 | 834 redirect = 1; |
835 break; | |
836 default: | |
837 mp_msg(MSGT_NETWORK,MSGL_ERR,"Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
838 goto err_out; |
15585 | 839 } |
840 } while( redirect ); | |
841 | |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
842 err_out: |
35308
f570e9faa3c7
Fix off-by-one errors in file descriptor validity checks.
reimar
parents:
35301
diff
changeset
|
843 if (fd >= 0) closesocket( fd ); |
21565
546cf4a6377a
Make sure stream->fd is set correct (esp. to -1 on error when fd is closed)
reimar
parents:
21542
diff
changeset
|
844 fd = -1; |
21542 | 845 http_free( http_hdr ); |
21776 | 846 http_hdr = NULL; |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
847 out: |
35309 | 848 stream->streaming_ctrl->data = http_hdr; |
21565
546cf4a6377a
Make sure stream->fd is set correct (esp. to -1 on error when fd is closed)
reimar
parents:
21542
diff
changeset
|
849 stream->fd = fd; |
21540
147c1c305f21
Fix lots and lots of potential memory/fd leaks in http_streaming_start
reimar
parents:
20784
diff
changeset
|
850 return res; |
15585 | 851 } |
852 | |
853 static int fixup_open(stream_t *stream,int seekable) { | |
16013 | 854 HTTP_header_t *http_hdr = stream->streaming_ctrl->data; |
16078
095e980cf7c0
Some ICY servers (e.g. http://broadcast.spnet.net:8000/darikhigh) do not set
reimar
parents:
16070
diff
changeset
|
855 int is_icy = http_hdr && http_get_field(http_hdr, "Icy-MetaInt"); |
16070 | 856 int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0; |
15585 | 857 |
858 stream->type = STREAMTYPE_STREAM; | |
16013 | 859 if(!is_icy && !is_ultravox && seekable) |
15585 | 860 { |
29920
4f740437ed2b
Finally rename the STREAM_SEEK define to MP_STREAM_SEEK, there are just too many
reimar
parents:
29263
diff
changeset
|
861 stream->flags |= MP_STREAM_SEEK; |
15585 | 862 stream->seek = http_seek; |
863 } | |
864 stream->streaming_ctrl->bandwidth = network_bandwidth; | |
16013 | 865 if ((!is_icy && !is_ultravox) || scast_streaming_start(stream)) |
15585 | 866 if(nop_streaming_start( stream )) { |
867 mp_msg(MSGT_NETWORK,MSGL_ERR,"nop_streaming_start failed\n"); | |
21567 | 868 if (stream->fd >= 0) |
869 closesocket(stream->fd); | |
870 stream->fd = -1; | |
15585 | 871 streaming_ctrl_free(stream->streaming_ctrl); |
872 stream->streaming_ctrl = NULL; | |
24257 | 873 return STREAM_UNSUPPORTED; |
15585 | 874 } |
875 | |
876 fixup_network_stream_cache(stream); | |
877 return STREAM_OK; | |
878 } | |
879 | |
880 static int open_s1(stream_t *stream,int mode, void* opts, int* file_format) { | |
881 int seekable=0; | |
882 | |
883 stream->streaming_ctrl = streaming_ctrl_new(); | |
884 if( stream->streaming_ctrl==NULL ) { | |
885 return STREAM_ERROR; | |
886 } | |
887 stream->streaming_ctrl->bandwidth = network_bandwidth; | |
35301
8f6d3f8ffa61
Add url_new_with_proxy function to reduce code duplication and memleaks.
reimar
parents:
35229
diff
changeset
|
888 stream->streaming_ctrl->url = url_new_with_proxy(stream->url); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
889 |
20185 | 890 mp_msg(MSGT_OPEN, MSGL_V, "STREAM_HTTP(1), URL: %s\n", stream->url); |
15585 | 891 seekable = http_streaming_start(stream, file_format); |
892 if((seekable < 0) || (*file_format == DEMUXER_TYPE_ASF)) { | |
21567 | 893 if (stream->fd >= 0) |
894 closesocket(stream->fd); | |
895 stream->fd = -1; | |
25135
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
896 if (seekable == STREAM_REDIRECTED) |
66f628d13442
Support stream redirection from http to mms, fix bug #927.
ulion
parents:
24257
diff
changeset
|
897 return seekable; |
15585 | 898 streaming_ctrl_free(stream->streaming_ctrl); |
899 stream->streaming_ctrl = NULL; | |
24257 | 900 return STREAM_UNSUPPORTED; |
15585 | 901 } |
902 | |
903 return fixup_open(stream, seekable); | |
904 } | |
905 | |
906 static int open_s2(stream_t *stream,int mode, void* opts, int* file_format) { | |
907 int seekable=0; | |
908 | |
909 stream->streaming_ctrl = streaming_ctrl_new(); | |
910 if( stream->streaming_ctrl==NULL ) { | |
911 return STREAM_ERROR; | |
912 } | |
913 stream->streaming_ctrl->bandwidth = network_bandwidth; | |
35301
8f6d3f8ffa61
Add url_new_with_proxy function to reduce code duplication and memleaks.
reimar
parents:
35229
diff
changeset
|
914 stream->streaming_ctrl->url = url_new_with_proxy(stream->url); |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28402
diff
changeset
|
915 |
20185 | 916 mp_msg(MSGT_OPEN, MSGL_V, "STREAM_HTTP(2), URL: %s\n", stream->url); |
15585 | 917 seekable = http_streaming_start(stream, file_format); |
918 if(seekable < 0) { | |
21567 | 919 if (stream->fd >= 0) |
920 closesocket(stream->fd); | |
921 stream->fd = -1; | |
15585 | 922 streaming_ctrl_free(stream->streaming_ctrl); |
923 stream->streaming_ctrl = NULL; | |
24257 | 924 return STREAM_UNSUPPORTED; |
15585 | 925 } |
926 | |
927 return fixup_open(stream, seekable); | |
928 } | |
929 | |
930 | |
25211 | 931 const stream_info_t stream_info_http1 = { |
15585 | 932 "http streaming", |
933 "null", | |
934 "Bertrand, Albeau, Reimar Doeffinger, Arpi?", | |
935 "plain http", | |
936 open_s1, | |
27847
28deb37052cd
Add a noicyx:// protocol to allow easier testing for misconfigured servers.
reimar
parents:
27834
diff
changeset
|
937 {"http", "http_proxy", "unsv", "icyx", "noicyx", NULL}, |
15585 | 938 NULL, |
939 0 // Urls are an option string | |
940 }; | |
941 | |
25211 | 942 const stream_info_t stream_info_http2 = { |
15585 | 943 "http streaming", |
944 "null", | |
945 "Bertrand, Albeu, Arpi? who?", | |
16932
c30e0970250c
fix typos: aslo->also, falback->fallback (they were just too annoying *g*)
reimar
parents:
16917
diff
changeset
|
946 "plain http, also used as fallback for many other protocols", |
15585 | 947 open_s2, |
948 {"http", "http_proxy", "pnm", "mms", "mmsu", "mmst", "rtsp", NULL}, //all the others as fallback | |
949 NULL, | |
950 0 // Urls are an option string | |
951 }; |