Thursday, December 27, 2007

There were a conversation (in Russian) about how to generate files' TTH hashes compatible with DC. I couldn't find any ready-to-use solution in the Internet done in PHP. And taking into account some reefs in the implementation, I decided to make the class I'd written public. PS: use it at your own risk :)
<?php

/**
 * @author Alexey Kupershtokh <alexey.kupershtokh@gmail.com>
 * @url http://kupershtokh.blogspot.com/2007/12/on-phpclub.html
 */
class TTH {
  private static $BASE32_ALPHABET 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  private static $tiger_hash null;
  private static $tiger_mhash null;

  /**
   * Generates DC-compatible TTH of a file.
   *
   * @param string $filename
   * @return string
   */
  public static function getTTH($filename) {
    $fp fopen($filename"rb");
    if($fp) {
      $i 1;
      $hashes = array();
      while(!feof($fp)) {
        $buf fread($fp1024);
        if ($buf || ($i == 1)) {
          $hashes[$i] = self::tiger("\0".$buf);
          $j 1;
          while($i % ($j 2) == 0) {
            $hashes[$i] = self::tiger("\1".$hashes[$i $j].$hashes[$i]);
            unset($hashes[$i $j]);
            $j round($j 2);
          }
          $i++;
        }
      }
      $k 1;
      while($i $k) {
        $k round($k 2);
      }
      for(; $i <= $k$i++) {
          $j 1;
          while($i % ($j 2) == 0) {
            if(isset($hashes[$i])) {
              $hashes[$i] = self::tiger("\1".$hashes[$i $j].$hashes[$i]);
            } elseif(isset($hashes[$i $j])) {
              $hashes[$i] = $hashes[$i $j];
            }
            unset($hashes[$i $j]);
            $j round($j 2);
          }
      }
      fclose($fp);

      return self::base32encode($hashes[$i-1]);
    }
  }

  /**
   * Generates a DC-compatible tiger hash (not TTH).
   * Automatically chooses between hash() and mhash().
   *
   * @param string $string
   * @return string
   */
  private static function tiger($string) {
    if (is_null(self::$tiger_hash)) {
       self::$tiger_hash function_exists("hash_algos") && in_array("tiger192,3"hash_algos());
    }
    if (self::$tiger_hash) {
      return self::tigerfix(hash("tiger192,3"$string1));
    }

    if (is_null(self::$tiger_mhash)) {
      self::$tiger_mhash function_exists("mhash");
    }
    if(self::$tiger_mhash) {
      return self::tigerfix(mhash(MHASH_TIGER$string));
    }

    trigger_error(E_USER_ERROR"Neither tiger hash function is available.");
  }

  /**
   * Repairs tiger hash for compatibility with DC.
   *
   * @url http://www.php.net/manual/en/ref.mhash.php#55737
   * @param string $binary_hash
   * @return string
   */ 
  private static function tigerfix($binary_hash) {
      $my_split str_split($binary_hash,8);
      $my_tiger ="";
      foreach($my_split as $key => $value) {
         $my_split[$key] = strrev($value);
         $my_tiger .= $my_split[$key];
      }
     return $my_tiger;
  }

  /**
   * Just a base32encode function :)
   *
   * @url http://www.php.net/manual/en/function.sha1-file.php#61741
   * @param string $input
   * @return string
   */
  private static function base32encode($input) {
    $output '';
    $position 0;
    $storedData 0;
    $storedBitCount 0;
    $index 0;
    while ($index strlen($input)) {
      $storedData <<= 8;
      $storedData += ord($input[$index]);
      $storedBitCount += 8;
      $index += 1;
      //take as much data as possible out of storedData
      while ($storedBitCount >= 5) {
        $storedBitCount -= 5;
        $output .= self::$BASE32_ALPHABET[$storedData >> $storedBitCount];
        $storedData &= ((<< $storedBitCount) - 1);
      }
    } //while
    //deal with leftover data
    if ($storedBitCount 0) {
      $storedData <<= (5-$storedBitCount);
      $output .= self::$BASE32_ALPHABET[$storedData];
    }
    return $output;
  }
}

?>

No comments: