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