Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / vorbis-tools / ogg123 / http_transport.c
bloba3f8f94a29e045c86525360ca7f622a5c5ad36fc
1 /********************************************************************
2 * *
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
6 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
7 * *
8 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001 *
9 * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS *
10 * http://www.xiph.org/ *
11 * *
12 ********************************************************************
14 last mod: $Id$
16 ********************************************************************/
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 #ifdef HAVE_CURL
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <signal.h>
28 #include <curl/curl.h>
29 #include <curl/easy.h>
30 #include <pthread.h>
32 #include "ogg123.h"
33 #include "transport.h"
34 #include "buffer.h"
35 #include "status.h"
36 #include "callbacks.h"
37 #include "i18n.h"
39 #define INPUT_BUFFER_SIZE 32768
41 extern stat_format_t *stat_format; /* FIXME Bad hack! Will fix after RC3! */
42 extern signal_request_t sig_request; /* Need access to global cancel flag */
44 typedef struct http_private_t {
45 int cancel_flag;
47 buf_t *buf;
49 pthread_t curl_thread;
51 CURL *curl_handle;
52 struct curl_slist *header_list;
53 char error[CURL_ERROR_SIZE];
55 data_source_t *data_source;
56 data_source_stats_t stats;
57 } http_private_t;
60 transport_t http_transport; /* Forward declaration */
62 /* -------------------------- curl callbacks ----------------------- */
64 size_t write_callback (void *ptr, size_t size, size_t nmemb, void *arg)
66 http_private_t *myarg = arg;
68 if (myarg->cancel_flag || sig_request.cancel)
69 return 0;
71 if (!buffer_submit_data(myarg->buf, ptr, size*nmemb))
72 return 0;
74 if (myarg->cancel_flag || sig_request.cancel)
75 return 0;
77 return size * nmemb;
80 int progress_callback (void *arg, size_t dltotal, size_t dlnow,
81 size_t ultotal, size_t ulnow)
83 http_private_t *myarg = arg;
84 print_statistics_arg_t *pstats_arg;
85 data_source_t *source = myarg->data_source;
87 if (myarg->cancel_flag || sig_request.cancel)
88 return -1;
90 pstats_arg = new_print_statistics_arg(stat_format,
91 source->transport->statistics(source),
92 NULL);
94 print_statistics_action(NULL, pstats_arg);
96 if (myarg->cancel_flag || sig_request.cancel)
97 return -1;
99 return 0;
103 /* -------------------------- Private functions --------------------- */
105 void set_curl_opts (http_private_t *private)
107 CURL *handle = private->curl_handle;
109 curl_easy_setopt(handle, CURLOPT_FILE, private);
110 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
111 curl_easy_setopt(handle, CURLOPT_URL, private->data_source->source_string);
113 if (inputOpts.ProxyPort)
114 curl_easy_setopt(handle, CURLOPT_PROXYPORT, inputOpts.ProxyPort);
115 if (inputOpts.ProxyHost)
116 curl_easy_setopt(handle, CURLOPT_PROXY, inputOpts.ProxyHost);
117 if (inputOpts.ProxyTunnel)
118 curl_easy_setopt (handle, CURLOPT_HTTPPROXYTUNNEL, inputOpts.ProxyTunnel);
120 #ifdef CURLOPT_MUTE
121 curl_easy_setopt(handle, CURLOPT_MUTE, 1);
122 #endif
123 curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, private->error);
124 curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, progress_callback);
125 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, private);
126 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);
127 curl_easy_setopt(handle, CURLOPT_USERAGENT, "ogg123/"VERSION);
128 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, private->header_list);
129 curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
133 void *curl_thread_func (void *arg)
135 http_private_t *myarg = (http_private_t *) arg;
136 CURLcode ret;
137 sigset_t set;
139 /* Block signals to this thread */
140 sigfillset (&set);
141 sigaddset (&set, SIGINT);
142 sigaddset (&set, SIGTSTP);
143 sigaddset (&set, SIGCONT);
144 if (pthread_sigmask (SIG_BLOCK, &set, NULL) != 0)
145 status_error(_("ERROR: Could not set signal mask."));
147 ret = curl_easy_perform((CURL *) myarg->curl_handle);
149 if (myarg->cancel_flag || sig_request.cancel) {
150 buffer_abort_write(myarg->buf);
151 ret = 0; // "error" was on purpose
152 } else
153 buffer_mark_eos(myarg->buf);
155 if (ret != 0)
156 status_error(myarg->error);
158 curl_easy_cleanup(myarg->curl_handle);
159 myarg->curl_handle = 0;
161 curl_slist_free_all(myarg->header_list);
162 myarg->header_list = NULL;
164 return (void *) ret;
168 /* -------------------------- Public interface -------------------------- */
170 int http_can_transport (char *source_string)
172 int tmp;
174 tmp = strchr(source_string, ':') - source_string;
175 return tmp < 10 &&
176 tmp + 2 < strlen(source_string) &&
177 !strncmp(source_string + tmp, "://", 3);
181 data_source_t* http_open (char *source_string, ogg123_options_t *ogg123_opts)
183 data_source_t *source;
184 http_private_t *private;
186 /* Allocate data source structures */
187 source = malloc(sizeof(data_source_t));
188 private = malloc(sizeof(http_private_t));
190 if (source != NULL && private != NULL) {
191 source->source_string = strdup(source_string);
192 source->transport = &http_transport;
193 source->private = private;
195 private->buf = buffer_create (ogg123_opts->input_buffer_size,
196 ogg123_opts->input_buffer_size *
197 ogg123_opts->input_prebuffer / 100.0,
198 NULL, NULL, /* No write callback, using
199 buffer in pull mode. */
200 0 /* Irrelevant */);
201 if (private->buf == NULL) {
202 status_error(_("ERROR: Unable to create input buffer.\n"));
203 exit(1);
206 private->curl_handle = NULL;
207 private->header_list = NULL;
208 private->data_source = source;
209 private->stats.transfer_rate = 0;
210 private->stats.bytes_read = 0;
211 private->stats.input_buffer_used = 0;
212 private->cancel_flag = 0;
214 } else {
215 fprintf(stderr, _("ERROR: Out of memory.\n"));
216 exit(1);
219 /* ogg123 only accepts Ogg files, and optionally FLAC as well */
220 #ifdef HAVE_LIBFLAC
221 private->header_list = curl_slist_append(NULL, "Accept: application/ogg, audio/ogg, video/ogg, audio/x-flac;q=0.9");
222 #else
223 private->header_list = curl_slist_append(NULL, "Accept: application/ogg, audio/ogg, video/ogg;q=0.9");
224 #endif
225 if (private->header_list == NULL)
226 goto fail;
228 /* Open URL */
229 private->curl_handle = curl_easy_init();
230 if (private->curl_handle == NULL)
231 goto fail;
233 set_curl_opts(private);
235 /* Start thread */
236 if (pthread_create(&private->curl_thread, NULL, curl_thread_func,
237 private) != 0)
238 goto fail;
241 stat_format[2].enabled = 0; /* remaining playback time */
242 stat_format[3].enabled = 0; /* total playback time */
243 stat_format[6].enabled = 1; /* Input buffer fill % */
244 stat_format[7].enabled = 1; /* Input buffer state */
246 return source;
249 fail:
250 if (private->curl_handle != NULL)
251 curl_easy_cleanup(private->curl_handle);
252 if (private->header_list != NULL)
253 curl_slist_free_all(private->header_list);
254 free(source->source_string);
255 free(private);
256 free(source);
258 return NULL;
262 int http_peek (data_source_t *source, void *ptr, size_t size, size_t nmemb)
265 http_private_t *private = source->private;
266 int items;
267 long start;
269 bytes_read = buffer_get_data(data->buf, ptr, size, nmemb);
271 private->stats.bytes_read += bytes_read;
274 return 0;
278 int http_read (data_source_t *source, void *ptr, size_t size, size_t nmemb)
280 http_private_t *private = source->private;
281 int bytes_read;
283 if (private->cancel_flag || sig_request.cancel)
284 return 0;
286 bytes_read = buffer_get_data(private->buf, ptr, size * nmemb);
288 private->stats.bytes_read += bytes_read;
290 return bytes_read;
294 int http_seek (data_source_t *source, long offset, int whence)
296 return -1;
300 data_source_stats_t *http_statistics (data_source_t *source)
302 http_private_t *private = source->private;
303 data_source_stats_t *data_source_stats;
304 buffer_stats_t *buffer_stats;
306 data_source_stats = malloc_data_source_stats(&private->stats);
307 data_source_stats->input_buffer_used = 1;
308 data_source_stats->transfer_rate = 0;
310 buffer_stats = buffer_statistics(private->buf);
311 data_source_stats->input_buffer = *buffer_stats;
312 free(buffer_stats);
314 return data_source_stats;
318 long http_tell (data_source_t *source)
320 return 0;
324 void http_close (data_source_t *source)
326 http_private_t *private = source->private;
328 private->cancel_flag = 1;
329 buffer_abort_write(private->buf);
330 pthread_join(private->curl_thread, NULL);
332 buffer_destroy(private->buf);
333 private->buf = NULL;
335 free(source->source_string);
336 free(source->private);
337 free(source);
341 transport_t http_transport = {
342 "http",
343 &http_can_transport,
344 &http_open,
345 &http_peek,
346 &http_read,
347 &http_seek,
348 &http_statistics,
349 &http_tell,
350 &http_close
353 #endif /* HAVE_CURL */