1#!/usr/local/bin/perl
2#
3
4## This file is part of the aMule Project
5##
6## Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org )
7## Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
8##
9## This program is free software; you can redistribute it and/or
10## modify it under the terms of the GNU General Public License
11## as published by the Free Software Foundation; either
12## version 2 of the License, or (at your option) any later version.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19## You should have received a copy of the GNU General Public License
20## along with this program; if not, write to the Free Software
21## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
22
23# Gimme a break, is my first perl app... (Kry)
24
25use warnings;
26use strict;
27
28eval "use File::Copy";
29if ($@) {
30	die "File::Copy perl module is required by the mldonkey_importer script.\n"
31	  . "If you want to use this script please install File::Copy from CPAN.\n";
32}
33
34my $exit_with_help;
35
36if (!($ARGV[0])) {
37	print "You must specify the mldonkey config folder (usually ~/.mldonkey).\n";
38	$exit_with_help = "true";
39}
40
41if (!($ARGV[1])) {
42	print "You must specify the aMule temp folder for output.\n";
43	$exit_with_help = "true";
44}
45
46if ($exit_with_help) {
47	die "Usage: importer2.pl mldonkey_config_folder amule_temp_folder.\n";
48}
49
50
51my $input_folder = $ARGV[0];
52
53my $output_folder = $ARGV[1];
54
55open(TEST,">" . $output_folder . "/test_file") or die "Unable to write to destination folder! Error: $!\n";
56close(TEST);
57unlink($output_folder . "/test_file");
58
59open(INFO, $input_folder . "/files.ini") or die "Cannot open input file" . $input_folder . "/files.ini for reading: $!";		# Open the file
60
61my $line="no";
62while ($line !~ /^\s*files\s*=\s*\[\s*$/) {
63	$line = <INFO>;
64	if (!($line)) {
65		die $input_folder . "/files.ini seems not to be a mldonkey files.ini\n";
66	}
67	chop $line;
68}
69
70#We're at the start of the downloading files section.
71# Read info for each file.
72
73my $number = 1;
74
75while ($line && ($line !~ /^.*};\].*$/)) {
76	print "Reading info for file $number\n";
77	&read_file_info;
78	print "End reading\n\n";
79	$number++;
80}
81
82close(INFO);
83
84sub read_file_info {
85	$line = <INFO>;
86
87	my @md4_list = ();
88	my @gap_list = ();
89	my $file_size = 0;
90	my $file_name = "";
91	my $part_file = "";
92	my $md4_hash = "";
93
94	my $done = "false";
95
96	while (($line) && ($line !~ /^\s*}.*/) && ($done ne "true")) {
97		chop $line;
98		if ($line =~ /.*file_network\s*=\s*(.*)$/) {
99			print "Network is $1\n";
100			if ($1 ne "Donkey") {
101				print "Cannot import non-ed2k part file, skipping\n";
102				while (($line) && ($line !~ /^\s*}.*/)) {
103					$line = <INFO>;
104					$done = "true";
105				}
106			}
107		}
108		if ($line =~ /^\s*file_size\s*=\s*(\d+)\s*$/) {
109			$file_size = $1;
110			print "File size: $file_size\n";
111		}
112		if ($line =~ /^\s*file_swarmer\s*=\s*\"(.*)\"\s*$/) {
113			$part_file = $1;
114			print "Part file to import: $part_file\n";
115		}
116		if ($line =~ /^\s*file_md4\s*=\s*\"?(([A-Z]|[0-9])+)\"?\s*$/) {
117			$md4_hash = $1;
118			print "File hash: $md4_hash\n";
119		}
120		if ($line =~ /^\s*file_filename\s*=\s*\"(.*)\"\s*$/) {
121			$file_name = $1;
122			print "File name: $file_name\n";
123		}
124		if ($line =~ /^\s*file_md4s\s*=\s*\[\s*$/) {
125			# Read the MD4 list
126			my $result = "";
127			do {
128				my $md4_line = <INFO>;
129				if ($md4_line =~ /^\s*\"?(([A-Z]|[0-9])+)\"?;\]?\s*$/) {
130					push(@md4_list,$1);
131					if ($md4_line =~ /^.*;\].*$/) {
132						$result = "done";
133					}
134				} else {
135					print "Malformed md4 hash line $md4_line";
136					@md4_list = ();
137					$result = "error";
138				}
139			} while (!($result));
140			if ($result eq "done") {
141				print "MD4 list: @md4_list\n";
142			}
143
144
145		}
146
147		if ($line =~ /^\s*file_present_chunks\s*=\s*\[\s*$/) {
148			# Read the gaps list
149			my $result = "";
150			my @ml_gaps = ();
151			do {
152				my $gaps_line = <INFO>;
153				if ($gaps_line =~ /^\s*\((\d+),\s*(\d+)\)(;|])\s*$/) {
154					push(@ml_gaps,$1);
155					push(@ml_gaps,$2);
156					if ($gaps_line =~ /^.*\)\].*$/) {
157						$result = "done";
158					}
159				} else {
160					print "Malformed gaps line $gaps_line";
161					$result = "error";
162				}
163			} while (!($result));
164
165			if ($result eq "done") {
166				# Process mldonkey gaps to aMule gaps
167				print "ML Gaps list: @ml_gaps\n";
168
169				@gap_list = &convert_gap_format($file_size,@ml_gaps);
170
171				print "aMule Gaps list: @gap_list\n";
172			}
173
174
175		}
176
177		if ($done ne "true") {
178			$line = <INFO>;
179		}
180	}
181
182	if ($done eq "true") {
183		print "File import result: false\n";
184	} else {
185		if ($file_name && $file_size && $md4_hash && $part_file) {
186			if (!(@md4_list)) {
187				print "WARNING: File has no md4 hashes list, imported file will have 0 bytes downloaded\n";
188			}
189
190			my $first_free_number = &get_first_free_number;
191
192			my $met_file = $output_folder . sprintf("/%03d.part.met",$first_free_number);
193
194			&create_met_file($met_file,$file_name,$file_size,$md4_hash,@md4_list,"---",@gap_list);
195
196			print "File $met_file imported successfully.\n";
197
198			my $from = $input_folder . "/" . $part_file;
199			my $destination = $output_folder . sprintf("/%03d.part",$first_free_number);
200			copy($from, $destination) or die "CRITICAL: File $from cannot be copied to $destination. Error: $!\n";
201
202		} else {
203			print "Not enough info to import file, sorry.\n";
204		}
205	}
206	$line;
207}
208
209sub create_met_file {
210
211	print "Parameters: @_\n";
212
213	#Open the new file
214	open(MET," > $_[0]");
215	binmode MET;
216
217	my $large_file = "";
218
219	# Met file version (1 byte)
220	if ($_[2] < 4290048000) {
221		# Not large file
222		$large_file = "no";
223		printf MET &byte_string(0xe0);
224	} else {
225		$large_file = "yes";
226		printf MET &byte_string(0xe2);
227	}
228	# File modification time. 0 to force aMule rehash. (4 bytes)
229	print MET &int32_string(0);
230
231	# MD4 hash (16 bytes)
232	print MET &hash_string($_[3]);
233
234	#Calculate number of MD4 hashes
235	my @md4_hashlist = ();
236
237	my $i = 4;
238
239	while ($_[$i] ne "---") {
240		push (@md4_hashlist,$_[$i]);
241		$i++;
242	}
243
244	$i++;
245
246	my @gaps_list = ();
247	while ($_[$i]) {
248		push(@gaps_list,$_[$i]);
249		$i++;
250	}
251
252	print "Write aMule gap list: @gaps_list\n";
253
254	my $md4_hashsize = @md4_hashlist;
255
256	print "MD4 hashlist size $md4_hashsize\n";
257
258	#Number of MD4 hashes (2 bytes)
259	print MET &int16_string($md4_hashsize);
260
261	#Write MD4 hashes (16 bytes * number of hashes)
262	my $md4_parthash = "";
263	foreach $md4_parthash (@md4_hashlist) {
264		print MET &hash_string($md4_parthash);
265	}
266
267	#Number of tags (4 bytes)
268
269	my $tags_number = 2; # Fixed tags (Name + Size)
270
271	$tags_number = $tags_number + @gaps_list;
272
273	print MET &int32_string($tags_number);
274
275	#Name tag (x bytes)
276
277	print MET &tag_string(2,0,0x01,$_[1]); # Tagtype string, id FT_FILENAME, value
278
279	#Size tag (x bytes)
280
281	if ($large_file eq "yes") {
282		print MET &tag_string(0x0b,0,0x02,$_[2]); # Tagtype UINT64, id FT_FILESIZE, value
283	} else {
284		print MET &tag_string(3,0,0x02,$_[2]); # Tagtype UINT32, id FT_FILESIZE, value
285	}
286
287	my $t = 0;
288
289	my $tag_type;
290	if ($large_file eq "yes") {
291		$tag_type = 0x0b;
292	} else {
293		$tag_type = 0x03;
294	}
295
296	while (@gaps_list[$t*2]) {
297		my $gap_start = @gaps_list[$t*2];
298		my $gap_end = @gaps_list[$t*2+1];
299
300		print "Gap $t start $gap_start end $gap_end\n";
301
302		print MET &tag_string($tag_type,1,sprintf("%c%d",0x09,$t),$gap_start);
303		print MET &tag_string($tag_type,1,sprintf("%c%d",0x0a,$t),$gap_end);
304
305		$t++;
306	}
307
308	close(MET);
309}
310
311sub byte_string {
312	sprintf("%c",$_[0]);
313}
314
315sub int16_string {
316	&byte_string($_[0] % 256) . &byte_string($_[0] / 256);
317}
318
319sub int32_string {
320	&int16_string($_[0] % 65536) . &int16_string($_[0] / 65536);
321}
322
323sub int64_string {
324	&int32_string($_[0] % 4294967296) . &int32_string($_[0] / 4294967296);
325}
326
327sub hash_string {
328	my $i = 0;
329	my $final_string = "";
330	while ($i < 32) {
331		$final_string = $final_string . &byte_string(hex(substr($_[0],$i,2)));
332		$i += 2;
333	}
334	$final_string;
335}
336
337sub tag_string {
338	# ONLY STRINGS AND UINT32/64 SUPPORTED
339
340	my $final_string = "";
341
342	# Tag type
343	$final_string = $final_string . &byte_string($_[0]);
344
345	if ($_[1] == 0) {
346		# Byte ID tag
347		$final_string = $final_string . &int16_string(1);
348		$final_string = $final_string . &byte_string($_[2]);
349	} else {
350		# String ID tag
351		$final_string = $final_string . &int16_string(length $_[2]) . $_[2];
352	}
353
354	if ($_[0] == 2) {
355		$final_string = $final_string . &int16_string(length $_[3]) . $_[3];
356	} else {
357		if ($_[0] == 3) {
358			# UINT32
359			$final_string = $final_string . &int32_string($_[3]);
360		} else {
361			if ($_[0] == 0x0b) {
362				# UINT64
363				$final_string = $final_string . &int64_string($_[3]);
364			}
365		}
366	}
367	$final_string;
368}
369
370sub convert_gap_format {
371	my $total_size = $_[0];
372
373	my @converted_gaps = ();
374
375	my $n = 1;
376
377	if ($_[1] != 0) {
378		push(@converted_gaps,0);
379		push(@converted_gaps,$_[1]);
380	}
381
382	$n++;
383
384	while ($_[$n+1]) {
385		push(@converted_gaps,$_[$n]);
386		push(@converted_gaps,$_[$n+1]);
387		$n += 2;
388	}
389
390	if ($_[$n] != $total_size) {
391		push(@converted_gaps,$_[$n]);
392		push(@converted_gaps,$total_size);
393	}
394
395	@converted_gaps;
396}
397
398sub get_first_free_number {
399	my $n = 1;
400
401	while (-f sprintf("$output_folder/%03d.part.met",$n)) {
402		$n++;
403	}
404
405	$n;
406}
407