1 #include <glib.h>
2 #include <curl/curl.h>
3 #include "urlgfe.h"
4 #include "download_backend.h"
5 #include "utility.h"
6
7 typedef struct _DownloadBackendCurl DownloadBackendCurl;
8
9 struct _DownloadBackendCurl
10 {
11 CURL* curl;
12 gdouble last_completed;
13 glong resume_from;
14
15 DownloadThread* thread;
16
17 char error_string[CURL_ERROR_SIZE + 1];
18 };
19
20
21 static int curl_progress_func (DownloadBackendCurl* backend,
22 double dl_total,
23 double dl_now,
24 double ul_total,
25 double ul_now);
26
url_post_encode(const char * pre_url,int len)27 char* url_post_encode (const char* pre_url, int len)
28 {
29 GString* gstring;
30 UrlPart urlp;
31
32 if (len==0)
33 len = strlen (pre_url);
34
35 url_part (&urlp, pre_url);
36 if (urlp.path_beg) {
37 gstring = g_string_sized_new (256);
38 g_string_append_len (gstring, pre_url, urlp.path_beg - pre_url);
39 pre_url = urlp.path_beg;
40 for (; len; len--, pre_url++) {
41 if ( *pre_url == '\n' )
42 continue;
43 else if ( *pre_url & 0x80 || *pre_url== ' ')
44 g_string_append_printf (gstring, "%c%X", '%', *pre_url);
45 else
46 g_string_append_c (gstring, *pre_url);
47 }
48 return g_string_free (gstring, FALSE);
49 }
50 else
51 return g_strndup (pre_url, len);
52 }
53
download_backend_global_init()54 void download_backend_global_init ()
55 {
56 curl_global_init (CURL_GLOBAL_ALL);
57 }
58
download_backend_global_clean()59 void download_backend_global_clean ()
60 {
61 curl_global_cleanup ();
62 }
63
download_backend_new(gpointer data)64 gpointer download_backend_new (gpointer data)
65 {
66 DownloadBackendCurl* backend;
67
68 backend = g_malloc (sizeof (DownloadBackendCurl));
69 backend->curl = curl_easy_init ();
70 backend->thread = data;
71
72 return backend;
73 }
74
download_backend_destroy(gpointer data)75 void download_backend_destroy (gpointer data)
76 {
77 DownloadBackendCurl* backend = data;
78
79 curl_easy_cleanup (backend->curl);
80
81 g_free (backend);
82 }
83
download_backend_run(gpointer backend,DownloadThread * dt)84 gint download_backend_run (gpointer backend, DownloadThread* dt)
85 {
86 DownloadBackendCurl* dbc = backend;
87 GString* filename = dt->out_filename;
88 FILE* file;
89 DownloadSetting* setting = DOWNLOAD_SETTING (dt->download_data);
90
91 CURL* curl = dbc->curl;
92 CURLcode curl_code;
93 struct curl_slist* slist = NULL;
94 // long curl_proxy_type;
95
96 // string data used by libcurl
97 // besure curl session finish when you release these.
98 gchar* encoded_url = NULL;
99 gchar* referrer = NULL;
100 gchar* proxy_user_pwd = NULL;
101
102 dbc->last_completed = 0.0;
103
104 // check file
105 file = fopen (filename->str, (dt->resumable)? "ab" : "wb");
106 if (file==NULL) {
107 download_thread_job_set_message (dt, _("File open failure."));
108 return DOWNLOAD_BACKEND_ERROR;
109 }
110
111 if (dt->resumable) {
112 fseek (file, 0, SEEK_END);
113 dbc->resume_from = ftell (file);
114 } else {
115 dbc->resume_from = 0;
116 }
117
118 // test code
119 // curl_easy_setopt (curl, CURLOPT_UNRESTRICTED_AUTH, 1);
120 curl_easy_setopt (curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)");
121 // curl_easy_setopt (curl, CURLOPT_USERAGENT, "Internet Explorer");
122 curl_easy_setopt (curl, CURLOPT_COOKIEFILE, "");
123
124 // set proxy ----------------------------------------------------
125 // clear proxy setting
126 curl_easy_setopt (curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
127 curl_easy_setopt (curl, CURLOPT_PROXY, NULL);
128 curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, NULL);
129
130 if (PROXY_SETTING (setting)->type != PROXY_TYPE_NONE) {
131 if (PROXY_SETTING (setting)->type == PROXY_TYPE_HTTP)
132 curl_easy_setopt (curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
133 else
134 curl_easy_setopt (curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
135 curl_easy_setopt (curl, CURLOPT_PROXY,
136 PROXY_SETTING (setting)->address);
137 curl_easy_setopt (curl, CURLOPT_PROXYPORT,
138 (long)PROXY_SETTING (setting)->port);
139
140 if (PROXY_SETTING (setting)->authentication) {
141 // set username and password
142 proxy_user_pwd = g_strdup_printf ("%s:%s",
143 STR_N2E (PROXY_SETTING (setting)->username),
144 STR_N2E (PROXY_SETTING (setting)->password));
145 curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd);
146 }
147 else if (PROXY_SETTING (setting)->type == PROXY_TYPE_SOCKS5) {
148 // Avoid crash when using socks5 without authentication.
149 curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, ":");
150 }
151 }
152
153 // set redirection
154 if (setting->n_redirect) {
155 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
156 curl_easy_setopt (curl, CURLOPT_MAXREDIRS, setting->n_redirect);
157 } else {
158 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 0);
159 }
160
161 // set referrer
162 // referrer = g_strdup_printf ("Referrer: %s", STR_N2E (setting->referer));
163 // slist = curl_slist_append (slist, referrer);
164 // curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist);
165 curl_easy_setopt (curl, CURLOPT_REFERER, STR_N2E (setting->referer));
166
167 // set url
168 encoded_url = url_post_encode (STR_N2E (setting->url), 0);
169 curl_easy_setopt (curl, CURLOPT_URL, encoded_url);
170
171 // set download data
172 curl_easy_setopt (curl, CURLOPT_FILE, file);
173 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, FALSE);
174 curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, curl_progress_func);
175 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, dbc);
176 curl_easy_setopt (curl, CURLOPT_RESUME_FROM, dbc->resume_from);
177 curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, dbc->error_string);
178
179 // perform curl
180 curl_code = curl_easy_perform (curl);
181 fclose (file);
182 curl_slist_free_all (slist);
183
184 // free string that used by libcurl
185 g_free (referrer);
186 g_free (proxy_user_pwd);
187 g_free (encoded_url);
188
189 // dt->time_state.
190 // set_job_speed (NULL);
191
192 if (curl_code == CURLE_OK) {
193 Download* dld = dt->download_data;
194
195 if (dld->completed + dbc->resume_from == dld->total + dbc->resume_from &&
196 dld->completed + dbc->resume_from != 0 )
197 {
198 return DOWNLOAD_BACKEND_OK;
199 }
200 else {
201 unlink (filename->str);
202 download_thread_job_set_message (dt, _("File length error. Try to increase redirection limit."));
203 return DOWNLOAD_BACKEND_ERROR;
204 }
205 }
206
207 printf ("Curl error %.2u: %s\n", curl_code, dbc->error_string);
208 download_thread_job_set_message (dt, dbc->error_string);
209
210 // check curl return code
211 switch (curl_code) {
212 // can resume
213 case CURLE_PARTIAL_FILE:
214 case CURLE_GOT_NOTHING:
215 dt->resumable = TRUE;
216 break;
217 // can't resume
218 case CURLE_HTTP_RANGE_ERROR:
219 dt->resumable = FALSE;
220 dbc->resume_from = 0;
221 break;
222 case CURLE_ABORTED_BY_CALLBACK:
223 return DOWNLOAD_BACKEND_ABORT;
224 // case CURLE_UNSUPPORTED_PROTOCOL:
225 // case CURLE_FAILED_INIT:
226 // case CURLE_URL_MALFORMAT:
227 // case CURLE_URL_MALFORMAT_USER:
228 // case CURLE_COULDNT_RESOLVE_PROXY:
229 // case CURLE_COULDNT_RESOLVE_HOST:
230 // case CURLE_COULDNT_CONNECT:
231 // case CURLE_FTP_WEIRD_SERVER_REPLY:
232 // case CURLE_FTP_ACCESS_DENIED:
233 // case CURLE_FTP_USER_PASSWORD_INCORRECT:
234 default:
235 unlink (filename->str);
236 return DOWNLOAD_BACKEND_ERROR;
237 }
238
239 return DOWNLOAD_BACKEND_CAN_RETRY;
240 }
241
curl_progress_func(DownloadBackendCurl * dbc,double dl_total,double dl_now,double ul_total,double ul_now)242 static int curl_progress_func (DownloadBackendCurl* dbc,
243 double dl_total,
244 double dl_now,
245 double ul_total,
246 double ul_now)
247 {
248 DownloadThread* dt = dbc->thread;
249 Download* download_data = dt->download_data;
250 gdouble timer = download_thread_get_timer (dt);
251 gdouble speed;
252
253 if (dl_now) {
254 download_thread_job_set_info_valid (dt, TRUE);
255 speed = (dl_now - dbc->last_completed) / timer;
256 download_data->speed = (download_data->speed + speed) /2;
257 download_data->completed = dl_now + dbc->resume_from;
258 download_data->total = dl_total + dbc->resume_from;
259 dbc->last_completed = dl_now;
260 // curl pass dl_total=0 sometimes.
261 if (download_data->total < download_data->completed)
262 download_data->total = download_data->completed;
263 }
264
265 // avoid div zero
266 if (download_data->total)
267 download_data->percent = (download_data->completed / download_data->total)*100;
268
269 download_thread_job_redraw (dt);
270 // return true when abort.
271 return (dt->thread_state >= DOWNLOAD_THREAD_SKIP);
272 }
273
274
275