1 #include <stdio.h>
2 #include "utility.h"
3 #include "urlgfe.h"
4 #include "download_thread.h"
5 #include "download_backend.h"
6 
7 static gpointer download_thread_func (DownloadThread* dt);
8 
9 #ifdef G_OS_WIN32
10 
11 #include <windows.h>
download_thread_retry_delay(DownloadThread * dt)12 void download_thread_retry_delay (DownloadThread* dt)
13 {
14 	int count = DOWNLOAD_SETTING (dt->download_data)->retry_delay*2;
15 
16 	while (dt->thread_state < DOWNLOAD_THREAD_SKIP && count) {
17 		Sleep (500);  // check state every 0.5 second.
18 		count--;
19 	}
20 }
21 
22 #else
download_thread_retry_delay(DownloadThread * dt)23 void download_thread_retry_delay (DownloadThread* dt)
24 {
25 	int count = DOWNLOAD_SETTING (dt->download_data)->retry_delay;
26 
27 	while (dt->thread_state < DOWNLOAD_THREAD_SKIP && count) {
28 		sleep (1);    // check state every 1 second.
29 		count--;
30 	}
31 }
32 
33 #endif
34 
35 
download_thread_new(Category * cate,GtkTreeIter * iter_download)36 DownloadThread* download_thread_new (Category* cate,
37                                      GtkTreeIter* iter_download)
38 {
39 	DownloadThread* dt = g_malloc (sizeof(DownloadThread));
40 
41 	g_static_rec_mutex_init (&dt->mutex);
42 
43 	// list
44 	dt->prev = NULL;
45 	dt->next = NULL;
46 
47 	// category
48 	dt->category = cate;
49 	category_ref (cate);
50 
51 	dt->out_filename = g_string_sized_new (128);
52 
53 	// DownloadStore
54 	dt->iter_download = *iter_download;
55 	download_store_get_data (&dt->category->waiting_store,
56 	                         &dt->iter_download,
57 	                         &dt->download_data);
58 	download_store_set_thread (&dt->category->waiting_store,
59 	                           &dt->iter_download, dt);
60 	download_ref (dt->download_data);
61 
62 
63 	// timer
64 	dt->timer       = g_timer_new ();
65 	dt->timer_count = 0.0;
66 	g_timer_stop (dt->timer);
67 
68 	dt->thread_state = DOWNLOAD_THREAD_READY;
69 
70 	category_thread_add (cate, dt);
71 	return dt;
72 }
73 
download_thread_free_resource(DownloadThread * dt)74 void download_thread_free_resource (DownloadThread* dt)
75 {
76 	printf ("thread free : %p\n", dt);
77 
78 	gdk_threads_enter ();
79 	category_thread_remove (dt->category, dt);
80 	category_unref (dt->category);
81 	gdk_threads_leave ();
82 
83 	g_string_free (dt->out_filename, TRUE);
84 	g_timer_destroy (dt->timer);
85 	g_static_rec_mutex_free (&dt->mutex);
86 
87 	g_free (dt);
88 }
89 
download_thread_activate(DownloadThread * dt)90 void download_thread_activate (DownloadThread* dt)
91 {
92 	download_thread_lock(dt);
93 
94 	if (dt->thread_state == DOWNLOAD_THREAD_READY ) {
95 		dt->thread_state = DOWNLOAD_THREAD_ACTIVE;
96 
97 		dt->gthread = g_thread_create ((GThreadFunc)download_thread_func,
98 		                               dt,TRUE,NULL);
99 	}
100 
101 	download_thread_unlock(dt);
102 }
103 
download_thread_skip(DownloadThread * dt)104 void download_thread_skip (DownloadThread* dt)
105 {
106 	download_thread_lock(dt);
107 
108 	if (dt->thread_state < DOWNLOAD_THREAD_SKIP )
109 		dt->thread_state = DOWNLOAD_THREAD_SKIP;
110 
111 	download_thread_unlock(dt);
112 }
113 
download_thread_stop(DownloadThread * dt)114 void download_thread_stop (DownloadThread* dt)
115 {
116 	download_thread_lock(dt);
117 
118 	if (dt->thread_state == DOWNLOAD_THREAD_JOB_ERASED )
119 		dt->thread_state = DOWNLOAD_THREAD_DESTROY;
120 	else if (dt->thread_state < DOWNLOAD_THREAD_STOP )
121 		dt->thread_state = DOWNLOAD_THREAD_STOP;
122 
123 	download_thread_unlock(dt);
124 }
125 
download_thread_job_erased(DownloadThread * dt)126 void download_thread_job_erased (DownloadThread* dt)
127 {
128 	download_thread_lock(dt);
129 
130 	if (dt->thread_state == DOWNLOAD_THREAD_STOP )
131 		dt->thread_state = DOWNLOAD_THREAD_DESTROY;
132 	else if (dt->thread_state < DOWNLOAD_THREAD_JOB_ERASED )
133 		dt->thread_state = DOWNLOAD_THREAD_JOB_ERASED;
134 
135 	download_thread_unlock(dt);
136 }
137 
download_thread_destroy(DownloadThread * dt)138 void download_thread_destroy (DownloadThread* dt)
139 {
140 	download_thread_lock(dt);
141 
142 	if (dt->thread_state < DOWNLOAD_THREAD_DESTROY )
143 		dt->thread_state = DOWNLOAD_THREAD_DESTROY;
144 
145 	download_thread_unlock(dt);
146 }
147 
download_thread_job_redraw(DownloadThread * dt)148 void download_thread_job_redraw (DownloadThread* dt)
149 {
150 	download_store_lock (&dt->category->waiting_store);
151 	download_thread_lock (dt);
152 
153 	if (dt->thread_state < DOWNLOAD_THREAD_JOB_ERASED) {
154 		gdk_threads_enter ();
155 #ifdef _WIN32
156 		// avoid crash in GTK+ for Win32 ...
157 		gtk_widget_queue_draw (GTK_WIDGET (dt->category->waiting_view.self));
158 #else
159 		download_store_row_changed (&dt->category->waiting_store,
160 		                            &dt->iter_download);
161 #endif
162 		gdk_threads_leave ();
163 	}
164 
165 	download_thread_unlock (dt);
166 	download_store_unlock (&dt->category->waiting_store);
167 }
168 
download_thread_test_file(DownloadThread * dt)169 gboolean download_thread_test_file (DownloadThread* dt)
170 {
171 	gint     temp;
172 	gint     directory_len;
173 	gboolean return_val = FALSE;
174 	GString* name = dt->out_filename;
175 	DownloadSetting* setting = DOWNLOAD_SETTING (dt->download_data);
176 	gchar*   utf8_file_name;
177 	gchar*   os_dir_name;
178 	gchar*   os_file_name;
179 
180 	if (setting->directory==NULL || setting->filename==NULL)
181 		return FALSE;
182 
183 	// remove '\n' from url
184 	temp = strlen (setting->url);
185 	if (setting->url[temp-1]=='\n')
186 		setting->url[temp-1] = 0;
187 	// remove '\n' from directory and filename before convert
188 	temp = strlen (setting->directory);
189 	if (setting->directory[temp-1]=='\n')
190 		setting->directory[temp-1] = 0;
191 	temp = strlen (setting->filename);
192 	if (setting->filename[temp-1]=='\n')
193 		setting->filename[temp-1] = 0;
194 
195 	os_dir_name = g_filename_from_utf8 (setting->directory, -1,
196 	                                    NULL, NULL, NULL);
197 	os_file_name = g_filename_from_utf8 (setting->filename, -1,
198 	                                     NULL, NULL, NULL);
199 	g_string_truncate (name, 0);
200 	g_string_append (name, os_dir_name);
201 
202 	download_thread_lock (dt);
203 	if (dt->thread_state < DOWNLOAD_THREAD_SKIP && os_dir_name) {
204 		if ( name->str[name->len-1] != G_DIR_SEPARATOR )
205 			g_string_append_c (name, G_DIR_SEPARATOR);
206 		directory_len = name->len;
207 
208 		g_string_append (name, os_file_name);
209 
210 		if (make_path_exist (os_dir_name) && os_file_name) {
211 			temp = 0;
212 			do {
213 				if (g_file_test (name->str, G_FILE_TEST_EXISTS)==FALSE) {
214 					return_val = TRUE;
215 					break;
216 				}
217 
218 				g_string_truncate (name, directory_len);
219 				g_string_append_printf (name, "%s.%u",
220 				                        setting->filename,
221 				                        temp++);
222 			} while (temp<100);
223 
224 			utf8_file_name = g_filename_to_utf8 (name->str + directory_len,
225 			                                     -1, NULL, NULL, NULL);
226 			download_setting_set_filename (setting, utf8_file_name);
227 			g_free (utf8_file_name);
228 			g_string_append (name, ".ug_");
229 		}
230 	}
231 	download_thread_unlock (dt);
232 
233 	g_free (os_dir_name);
234 	g_free (os_file_name);
235 
236 	download_thread_job_redraw (dt);
237 
238 	return return_val;
239 }
240 
download_thread_load_next_job(DownloadThread * dt)241 gboolean download_thread_load_next_job (DownloadThread* dt)
242 {
243 	gboolean return_val = FALSE;
244 
245 	download_store_lock (&dt->category->waiting_store);
246 	download_thread_lock (dt);
247 
248 	if (dt->thread_state==DOWNLOAD_THREAD_JOB_ERASED
249 	        || dt->thread_state < DOWNLOAD_THREAD_STOP)
250 	{
251 		dt->thread_state = DOWNLOAD_THREAD_ACTIVE;
252 		gdk_threads_enter ();
253 		if (category_thread_count_request (dt->category) >=0
254 		    && download_store_find_waiting (&dt->category->waiting_store,
255 		                                    &dt->iter_download) )
256 		{
257 			download_store_get_data (&dt->category->waiting_store,
258 			                         &dt->iter_download,
259 			                         &dt->download_data);
260 			download_store_set_thread (&dt->category->waiting_store,
261 			                           &dt->iter_download,
262 			                           dt);
263 			download_ref (dt->download_data);
264 
265 			return_val = TRUE;
266 		}
267 		gdk_threads_leave ();
268 	}
269 
270 	download_thread_unlock (dt);
271 	download_store_unlock (&dt->category->waiting_store);
272 
273 	return return_val;
274 }
275 
276 
download_thread_get_timer(DownloadThread * dt)277 gdouble download_thread_get_timer (DownloadThread* dt)
278 {
279 	if (dt->timer_count==0) {
280 		g_timer_start (dt->timer);
281 		dt->timer_count = 1.0;
282 	}
283 	else {
284 		dt->timer_count = g_timer_elapsed (dt->timer, NULL);
285 		g_timer_start (dt->timer);
286 	}
287 
288 	return dt->timer_count;
289 }
290 
291 // ===========================================================================
292 
download_thread_func(DownloadThread * dt)293 static gpointer download_thread_func (DownloadThread* dt)
294 {
295 	gint      backend_code;
296 	guint     n_retry;
297 	gpointer  backend;
298 	gchar*    temp_string;
299 
300 	backend = download_backend_new (dt);
301 
302 	do {
303 		if (download_thread_test_file (dt)==FALSE) {
304 			download_thread_job_set_state (dt, DOWNLOAD_STATE_ERROR);
305 			download_thread_job_set_message (dt, _("Filename error or repeat."));
306 			download_thread_job_redraw (dt);
307 
308 			download_store_lock (&dt->category->waiting_store);
309 			download_thread_lock (dt);
310 			gdk_threads_enter ();
311 			download_store_set_thread (&dt->category->waiting_store,
312 			                           &dt->iter_download, NULL);
313 			gdk_threads_leave ();
314 			download_thread_unlock (dt);
315 			download_store_unlock (&dt->category->waiting_store);
316 
317 			continue;
318 		}
319 
320 		dt->resumable = TRUE;
321 		n_retry = 0;
322 
323 		// download & retry loop
324 		while (1) {
325 			if (dt->thread_state >= DOWNLOAD_THREAD_SKIP) {
326 				download_thread_job_set_state (dt, DOWNLOAD_STATE_PAUSE);
327 				break;
328 			}
329 			if (n_retry > DOWNLOAD_SETTING(dt->download_data)->n_retries) {
330 				download_thread_job_set_state (dt, DOWNLOAD_STATE_ERROR);
331 				download_thread_job_set_message (dt, _("Retry too many times."));
332 				break;
333 			}
334 			download_thread_job_set_speed (dt, 0);
335 			download_thread_job_set_state (dt, DOWNLOAD_STATE_EXECUTING);
336 			download_thread_job_set_retry (dt, n_retry);
337 			download_thread_job_set_total (dt, 0);
338 			download_thread_job_set_completed (dt, 0);
339 			download_thread_job_set_percent (dt, 0);
340 			download_thread_job_redraw (dt);
341 			n_retry++;
342 
343 			dt->timer_count = 0.0;
344 			// begin download here
345 			backend_code = download_backend_run (backend, dt);
346 
347 			// check download backend return code
348 			if (backend_code==DOWNLOAD_BACKEND_ABORT) {
349 				download_thread_job_set_state (dt, DOWNLOAD_STATE_PAUSE);
350 				break;
351 			}
352 			else if (backend_code==DOWNLOAD_BACKEND_OK) {
353 				temp_string = g_strndup (dt->out_filename->str,
354 				                         dt->out_filename->len-4);
355 				rename (dt->out_filename->str, temp_string);
356 				g_free (temp_string);
357 				download_thread_job_set_state (dt, DOWNLOAD_STATE_COMPLETED);
358 				break;
359 			}
360 			else if (backend_code==DOWNLOAD_BACKEND_ERROR) {
361 				download_thread_job_set_state (dt, DOWNLOAD_STATE_ERROR);
362 				break;
363 			}
364 
365 			download_thread_job_set_state (dt, DOWNLOAD_STATE_RETRY);
366 			download_thread_job_redraw (dt);
367 			download_thread_retry_delay (dt);
368 		}
369 		download_thread_job_redraw (dt);
370 
371 		// do some thing after download
372 		download_unref (dt->download_data);
373 
374 		download_store_lock (&dt->category->waiting_store);
375 		download_thread_lock (dt);
376 		if (dt->thread_state < DOWNLOAD_THREAD_JOB_ERASED)
377 		{
378 			gdk_threads_enter ();
379 			download_store_set_thread (&dt->category->waiting_store,
380 			                           &dt->iter_download, NULL);
381 			if (backend_code==DOWNLOAD_BACKEND_OK) {
382 				download_store_move_to_store (&dt->category->waiting_store,
383 				                              &dt->category->completed_store,
384 				                              &dt->iter_download);
385 				category_changed_with_children (dt->category, CATEGORY_COMPLETED);
386 			}
387 			gdk_threads_leave ();
388 		}
389 		download_thread_unlock (dt);
390 		download_store_unlock (&dt->category->waiting_store);
391 
392 	} while (download_thread_load_next_job (dt));
393 
394 	download_backend_destroy (backend);
395 	download_thread_free_resource (dt);
396 	return NULL;
397 }
398 
399