st-transfer.c

来自「linux下网络收音机的源码」· C语言 代码 · 共 1,115 行 · 第 1/2 页

C
1,115
字号
/* * Copyright (c) 2002, 2003, 2004 Jean-Yves Lefort * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include <string.h>#include <stdlib.h>#include <curl/curl.h>#ifdef ST_REGRESSION_TEST#define _(String)		(String)#define st_notice		g_warning#else#include <glib/gi18n.h>#include <gtk/gtk.h>#include "sgtk-auth-dialog.h"#include "st-dialog-api.h"#include "st-settings.h"#include "st-thread.h"#include "st-util-api.h"#include "st-shell.h"#endif /* ST_REGRESSION_TEST */#include "sg-util.h"#include "st-transfer.h"#define AGENT_STRING			"streamtuner"/* maintain compatibility with old libcurl versions */#ifndef CURLOPT_WRITEDATA#define CURLOPT_WRITEDATA		CURLOPT_FILE#endif#ifndef CURLOPT_HEADERDATA#define CURLOPT_HEADERDATA		CURLOPT_WRITEHEADER#endif/* * RFC 2616 3.7.1 says that if no charset is provided, media subtypes * of the "text" type are defined to have a default charset value of * "ISO-8859-1". */#define CHARSET(header, body) ((body) ? (body) : ((header) ? (header) : "ISO-8859-1"))/*** type definitions ********************************************************//* * libcurl already provides this prototype as curl_write_callback, but * we are not gonna rely on it as it's undocumented */typedef size_t (CurlCallback) (const char *buffer,			       size_t size,			       size_t nitems,			       gpointer data);typedef struct{#ifndef ST_REGRESSION_TEST  STThread			*thread;#endif  double			downloaded;  const char			*url;  STTransferFlags		flags;  STTransferSession		*session;  char				error[CURL_ERROR_SIZE];  CurlCallback			*header_cb;  gpointer			header_data;  CurlCallback			*body_cb;  gpointer			body_data;  curl_proxytype		proxy_type;  char				*proxy_server;  int				proxy_port;  char				*proxy_userpwd;  char				*header_charset;  char				*body_charset;} STTransfer;struct _STTransferSession{  CURL				*handle;};typedef enum{  ST_TRANSFER_SECTION_HEADERS,  ST_TRANSFER_SECTION_BODY} STTransferSection;typedef struct{  STTransfer			*transfer;  STTransferSection		section;  GString			*line;  char				terminator[2]; /* \n\0, \r\n or \r\0 */  STTransferLineCallback	line_cb;  gpointer			line_cb_data;} STTransferLineData;/*** function declarations ***************************************************/static gboolean st_transfer_perform (STTransfer *transfer, GError **err);static gboolean st_transfer_set_proxy_settings (STTransfer *transfer);static void st_transfer_free_proxy_settings (STTransfer *transfer);#ifndef ST_REGRESSION_TESTstatic char *st_transfer_get_hostname (const char *url);#endifstatic gboolean st_transfer_progress_cb (gpointer data,					 double dltotal,					 double dlnow,					 double ultotal,					 double ulnow);static gboolean st_transfer_session_get_internal (STTransferSession *session,						  const char *url,						  STTransferFlags flags,						  char **headers,						  char **body,						  GError **err);static char *st_transfer_get_header_charset (const char *header);static char *st_transfer_get_body_charset (const char *body);static void st_transfer_line_data_init (STTransferLineData *data,					STTransfer *transfer,					STTransferSection section,					STTransferLineCallback cb,					gpointer cb_data);static void st_transfer_line_data_flush (STTransferLineData *data);static void st_transfer_line_data_free (STTransferLineData *data,					gboolean flush);static gboolean st_transfer_session_get_by_line_internal (STTransferSession *session,							  const char *url,							  STTransferFlags flags,							  STTransferLineCallback header_cb,							  gpointer header_data,							  STTransferLineCallback body_cb,							  gpointer body_data,							  GError **err);static size_t st_transfer_session_get_cb (const char *buffer,					  size_t size,					  size_t nitems,					  gpointer data);static size_t st_transfer_session_get_binary_cb (const char *buffer,						 size_t size,						 size_t nitems,						 gpointer data);static size_t st_transfer_session_get_by_line_cb (const char *buffer,						  size_t size,						  size_t nitems,						  gpointer data);/*** API implementation ******************************************************//** * st_transfer_session_new: * * Creates a new transfer session. * * Return value: the new session, which should be freed with * st_transfer_session_free() when no longer needed. **/STTransferSession *st_transfer_session_new (void){  STTransferSession *session;  session = g_new(STTransferSession, 1);  session->handle = curl_easy_init();  return session;}/** * st_transfer_session_free: * @session: the transfer session to destroy. * * Destroys @session. **/voidst_transfer_session_free (STTransferSession *session){  g_return_if_fail(session != NULL);  curl_easy_cleanup(session->handle);  g_free(session);}/* * Deprecated. */char *st_transfer_get_full (const char *url, GError **err){  STTransferSession *session;  gboolean status;  char *body;  g_return_val_if_fail(url != NULL, FALSE);  session = st_transfer_session_new();  status = st_transfer_session_get_internal(session, url, 0, NULL, &body, err);  st_transfer_session_free(session);  return status ? body : NULL;}/** * st_transfer_session_get: * @session: a transfer session. * @url: the URL of the data to get. * @flags: the transfer flags. Valid flags are #ST_TRANSFER_UTF8, * #ST_TRANSFER_PARSE_HTTP_CHARSET and * #ST_TRANSFER_PARSE_HTML_CHARSET. * @headers: a location to return the header data, or %NULL. The * returned data is guaranteed to be valid ASCII. * @body: a location to return the body data, or %NULL. If * #ST_TRANSFER_UTF8 is used, the returned data is guaranteed to be * valid UTF-8. * @err: a location to store errors, or %NULL. * * Gets the data located at @url. * * Return value: %FALSE if there was an error (in such case, @err will * be set). If %TRUE is returned, strings will be stored at @headers * and @body. They should be freed when no longer needed. **/gbooleanst_transfer_session_get (STTransferSession *session,			 const char *url,			 STTransferFlags flags,			 char **headers,			 char **body,			 GError **err){  gboolean status;  g_return_val_if_fail(session != NULL, FALSE);  g_return_val_if_fail(url != NULL, FALSE);  g_return_val_if_fail((flags & ST_TRANSFER_PASS_NEWLINE) == 0, FALSE);  status = st_transfer_session_get_internal(session, url, flags, headers, body, err);  return status;}/** * st_transfer_session_get_binary: * @session: a transfer session. * @url: the URL of the data to get. * @flags: must be 0. * @headers: a location to return the header data, or %NULL. The * returned data is guaranteed to be valid ASCII. * @body: a location to return the body data, or %NULL. If set, * @body_len must also be set. * @body_len: a location to return the body length, or %NULL. * @err: a location to store errors, or %NULL. * * Gets the binary data located at @url. * * Return value: %FALSE if there was an error (in such case, @err will * be set). If %TRUE is returned, a string will be stored at @headers * and a character buffer will be stored at @body. They both should be * freed when no longer needed. **/gbooleanst_transfer_session_get_binary (STTransferSession *session,				const char *url,				STTransferFlags flags,				char **headers,				guint8 **body,				unsigned int *body_len,				GError **err){  STTransfer transfer = { 0, };  GString *header_string;  GByteArray *body_array;  gboolean status;  g_return_val_if_fail(session != NULL, FALSE);  g_return_val_if_fail(url != NULL, FALSE);  g_return_val_if_fail((flags & ST_TRANSFER_PASS_NEWLINE) == 0, FALSE);  g_return_val_if_fail((flags & ST_TRANSFER_UTF8) == 0, FALSE);  g_return_val_if_fail((flags & ST_TRANSFER_PARSE_HTTP_CHARSET) == 0, FALSE);  g_return_val_if_fail((flags & ST_TRANSFER_PARSE_HTML_CHARSET) == 0, FALSE);    transfer.url = url;  transfer.flags = flags;  transfer.session = session;  if (headers)    {      header_string = g_string_new(NULL);      transfer.header_cb = st_transfer_session_get_cb;      transfer.header_data = header_string;    }  if (body)    {      body_array = g_byte_array_new();      transfer.body_cb = st_transfer_session_get_binary_cb;      transfer.body_data = body_array;    }    status = st_transfer_perform(&transfer, err);  if (status)    {      if (headers)	{	  if (sg_ascii_validate(header_string->str))	    *headers = header_string->str;	  else	    {	      g_set_error(err, 0, 0, _("invalid ASCII in HTTP headers"));	      status = FALSE;	    }	}      if (body && status)	{	  *body = body_array->data;	  *body_len = body_array->len;	}    }  if (headers)    g_string_free(header_string, ! status);  if (body)    g_byte_array_free(body_array, ! status);  return status;}/* * Deprecated */char *st_transfer_get_full_with_session (STTransferSession *session,				   const char *url,				   GError **err){  gboolean status;  char *body;  g_return_val_if_fail(session != NULL, FALSE);  g_return_val_if_fail(url != NULL, FALSE);  status = st_transfer_session_get_internal(session, url, 0, NULL, &body, err);    return status ? body : NULL;}static gbooleanst_transfer_session_get_internal (STTransferSession *session,				  const char *url,				  STTransferFlags flags,				  char **headers,				  char **body,				  GError **err){  STTransfer transfer = { 0, };  GString *header_string = NULL;  GString *body_string;  gboolean status;  g_return_val_if_fail(session != NULL, FALSE);  g_return_val_if_fail(url != NULL, FALSE);    transfer.url = url;  transfer.flags = flags;  transfer.session = session;  if (headers || flags & (ST_TRANSFER_UTF8 | ST_TRANSFER_PARSE_HTTP_CHARSET))    {      header_string = g_string_new(NULL);      transfer.header_cb = st_transfer_session_get_cb;      transfer.header_data = header_string;    }  if (body)    {      body_string = g_string_new(NULL);      transfer.body_cb = st_transfer_session_get_cb;      transfer.body_data = body_string;    }    status = st_transfer_perform(&transfer, err);  if (status)    {      if (header_string && ! sg_ascii_validate(header_string->str))	{	  g_set_error(err, 0, 0, _("invalid ASCII in HTTP headers"));	  status = FALSE;	}            if (status && flags & ST_TRANSFER_UTF8)	{	  char *header_charset = NULL;	  	  if (flags & ST_TRANSFER_PARSE_HTTP_CHARSET)	    {	      char **lines;	      int i;	      	      lines = g_strsplit(header_string->str, "\r\n", 0);	      for (i = 0; lines[i] && ! header_charset; i++)		header_charset = st_transfer_get_header_charset(lines[i]);	      g_strfreev(lines);	    }	  if (body)	    {	      char *body_charset;	      char *converted;	      body_charset = flags & ST_TRANSFER_PARSE_HTML_CHARSET		? st_transfer_get_body_charset(body_string->str)		: NULL;	      converted = g_convert(body_string->str,				    body_string->len,				    "UTF-8",				    CHARSET(header_charset, body_charset),				    NULL,				    NULL,				    err);	      if (converted)		{		  g_string_assign(body_string, converted);		  g_free(converted);		}	      else		status = FALSE;	      g_free(body_charset);	    }	  g_free(header_charset);	}      if (status)	{	  if (headers)	    *headers = header_string->str;	  if (body)	    *body = body_string->str;	}    }  if (header_string)    g_string_free(header_string, ! (headers && status));  if (body)    g_string_free(body_string, ! status);  return status;}/* * Deprecated. */gbooleanst_transfer_get_lines (const char *url,		       STTransferLineCallback cb,		       gpointer data,		       GError **err){  STTransferSession *session;  gboolean status;  g_return_val_if_fail(url != NULL, FALSE);  g_return_val_if_fail(cb != NULL, FALSE);  session = st_transfer_session_new();  status = st_transfer_session_get_by_line_internal(session, url, 0, NULL, NULL, cb, data, err);  st_transfer_session_free(session);  return status;}/** * st_transfer_session_get_by_line: * @session: a transfer session. * @url: the URL of the data to get. * @flags: the transfer flags. Valid flags are * #ST_TRANSFER_PASS_NEWLINE, #ST_TRANSFER_UTF8, * #ST_TRANSFER_PARSE_HTTP_CHARSET and * #ST_TRANSFER_PARSE_HTML_CHARSET. * @header_cb: a function to call when a header line arrives, or * %NULL. The line passed to the function is guaranteed to be valid * ASCII. * @header_data: data to pass to @header_cb. * @body_cb: a function to call when a body line arrives, or %NULL. If * #ST_TRANSFER_UTF8 is used, the line passed to the function is * guaranteed to be valid UTF-8. * @body_data: data to pass to @body_cb. * @err: a location to store errors, or %NULL. * * Gets the data located at @url, splitting it into lines and passing * them to @header_cb and @body_cb. * * Return value: %FALSE if there was an error (in such case, @err will * be set). **/gbooleanst_transfer_session_get_by_line (STTransferSession *session,				 const char *url,				 STTransferFlags flags,				 STTransferLineCallback header_cb,				 gpointer header_data,				 STTransferLineCallback body_cb,				 gpointer body_data,				 GError **err){  gboolean status;  g_return_val_if_fail(session != NULL, FALSE);  g_return_val_if_fail(url != NULL, FALSE);  status = st_transfer_session_get_by_line_internal(session, url, flags, header_cb, header_data, body_cb, body_data, err);  return status;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?