<?php
// $Id: import.inc,v 1.1.2.5 2007/03/31 19:02:51 z0rac Exp $
// original: locale.inc,v 1.105 2007/01/05 05:32:22 unconed Exp

function _update_locale_import_translations($modules, $general) {
  $count = array(0, 0);
  foreach (_update_locale_files($modules, $general) as $lang => $files) {
    $old = $count;
    foreach ($files as $file) {
      if ($changed = _update_locale_import($file, $lang)) {
        foreach ($changed as $i => $v) {
          $count[$i] += $v;
        }
      }
    }
    if ($count != $old) {
      cache_clear_all("locale:$lang", 'cache');
    }
  }
  if ($count[0] || $count[1]) {
    menu_rebuild();
    drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.', array('%number' => $count[0], '%update' => $count[1])));
  }
}

function _update_locale_files($modules, $general) {
  global $profile;
  $prof = isset($profile) ? $profile : variable_get('install_profile', 'default');
  $prof = "./profiles/$prof/translations";

  $langs = locale_supported_languages();
  unset($langs['name']['en']);
  $langs = array_keys($langs['name']);
  if (!$langs) return array();

  $files = array();
  if ($general) {
    foreach ($langs as $lang) {
      $files[$lang][] = (object)array(
        'filename' => '(drupal)',
        'filepath' => "$prof/$lang/$lang.po",
        'core' => TRUE
      );
    }
  }
  static $core = array(
    'aggregator', 'block', 'blog', 'blogapi', 'book', 'color', 'comment', 'contact',
    'drupal', 'filter', 'forum', 'help', 'legacy', 'locale', 'menu', 'node', 'path',
    'ping', 'poll', 'profile', 'search', 'statistics', 'system', 'taxonomy', 'throttle',
    'tracker', 'upload', 'user', 'watchdog'
  );
  foreach (array_diff((array)$modules, $core) as $module) {
    $dir = './'. drupal_get_path('module', $module);
    if ($last = strpos($dir, '/modules/')) {
      if ($last = strpos($dir, '/', $last + 9)) {
        $dir = substr($dir, 0, $last);
      }
      $name = basename($dir);
      foreach ($langs as $lang) {
        $path = "$prof/$lang/modules/$name.po";
        $files[$lang][] = (object)array(
          'filename' => $name,
          'filepath' => file_exists($path) ? $path : "$dir/po/$lang.po"
        );
      }
    }
  }
  return $files;
}

function _update_locale_import_cvs(&$file, $lang) {
  if ($repo = variable_get('update_locale_cvs', '')) {
    static $cvs = NULL;

    if (is_null($cvs)) {
      $cvs = FALSE;
      if ($h = popen('cvs -v', 'r')) {
        if (fgets($h) !== FALSE) {
          $cvs = 'cvs -z3 -Q';
        }
        pclose($h);
      }
    }

    $path = "translations/$lang/". ($file->core ? $lang : 'modules/'. $file->filename) .'.po';
    $tag = VERSION;
    $tag = 'DRUPAL-'. $tag[0];
    if ($cvs) {
      if ($h = popen("$cvs -d$repo rlog -N -r$tag $path", 'r')) {
        $s = fgets($h, 10240);
        for (; $s !== FALSE; $s = fgets($h, 10240)) {
          if (strpos($s, 'date:') !== FALSE) {
            if (preg_match('/(?:^|;)\s*date:(.+?)(?:;|$)/i', $s, $match)) {
              $time = strtotime($match[1]);
              break;
            }
          }
        }
        pclose($h);
      }
      if ($time) {
        if ($time > $file->time and $h = popen("$cvs -d$repo co -p -r$tag $path", 'r')) {
          $file->time = $time;
          $file->handle = $h;
          $file->fclose = 'pclose';
        }
        return TRUE;
      }
    }
    else {
      return _update_locale_import_cvsemu($file, $repo, $path, $tag);
    }
  }
  return FALSE;
}

function _update_locale_import_cvsemu(&$file, $repo, $path, $tag) {
  $cvsroot = explode(':', $repo, 4);
  if ($cvsroot[1] == 'pserver') {
    list($acc, $host) = explode('@', $cvsroot[2], 2);
    list($port, $root) = explode('/', $cvsroot[3], 2);
    $root = '/'. $root;
    if ($h = fsockopen($host, $port ? $port : 2401)) {
      fputs($h, "BEGIN AUTH REQUEST\n$root\n$acc\nA\nEND AUTH REQUEST\n");
      $s = fgets($h);
      if ($s == "I LOVE YOU\n") {
        $resp = array(
          'ok', 'error', 'Valid-requests', 'Checked-in', 'Updated', 'Merged', 'Removed',
          'M', 'E', 'F'
        );
        fputs($h, "Root $root\nValid-responses ". implode(' ', $resp) ."\nvalid-requests\n");
        $s = fgets($h, 10240);
        if ($s !== FALSE) {
          $s = fgets($h);
        }
        if ($s == "ok\n") {
          $req = 'UseUnchanged';
          $req .= implode("\nGlobal_option ", array('', '-q', '-Q'));
          $req .= implode("\nArgument ", array('', '-N', "-r$tag:$tag", '--', $path));
          $req .= "\nrlog\n";
          fputs($h, $req);
          $s = fgets($h, 10240);
          for (; $s[1] == ' '; $s = fgets($h, 10240)) {
            if (!$time && $s[0] == 'M' && strpos($s, 'date:') !== FALSE) {
              if (preg_match('/(?:^M|;)\s*date:(.+?)(?:;|$)/i', $s, $match)) {
                $time = strtotime($match[1]);
              }
            }
          }
          if ($s == "ok\n" && $time) {
            if ($time > $file->time) {
              $req = trim(implode("\nGlobal_option ", array('', '-n', '-q', '-Q')));
              $req .= implode("\nArgument ", array('', '-npNP', "-r$tag", '--', $path));
              $req .= "\nco\n";
              fputs($h, $req);
              $file->time = $time;
              $file->handle = $h;
              $file->fgets = '_update_locale_import_cvsemu_fgets';
            }
            return TRUE;
          }
        }
      }
      fclose($h);
    }
  }
  return FALSE;
}

function _update_locale_import_cvsemu_fgets($handle, $size = 1024) {
  $s = fgets($handle, $size + 2);
  for (; $s[1] == ' '; $s = fgets($handle, $size + 2)) {
    if ($s[0] == 'M') {
      return substr($s, 2);
    }
  }
  return FALSE;
}

function _update_locale_import($file, $lang) {
  include_once './includes/locale.inc';
  $file->time = db_result(db_query("SELECT ftime FROM {update_locale_file} WHERE name='%s' AND locale='%s'", $file->filename, $lang));
  if (!_update_locale_import_cvs($file, $lang) && file_exists($file->filepath)) {
    $mtime = filemtime($file->filepath);
    if ($mtime > $file->time) {
      $file->time = $mtime;
      $file->handle = fopen($file->filepath, 'rb');
      if (!$file->handle) {
        _locale_import_message('The translation import failed, because the file %filename could not be read.', $file);
      }
    }
  }
  if ($file->handle) {
    if (!ini_get('safe_mode')) {
      set_time_limit(240);
    }
    $file->count = array(0, 0);
    if (_update_locale_parse_po($file, $lang)) {
      if ($file->header) {
        if ($pf = _locale_import_parse_plural_forms($file->header['Plural-Forms'], $file->filename)) {
          db_query("UPDATE {locales_meta} SET plurals=%d, formula='%s' WHERE locale='%s'", $pf[0], $pf[1], $lang);
        }
        if ($file->header['PO-Revision-Date']) {
          $ptime = strtotime($file->header['PO-Revision-Date']);
        }
      }
      else {
        _locale_import_message('The translation file %filename appears to have a missing or malformed header.', $file);
      }
      db_query("UPDATE {update_locale_file} SET translator='%s', team='%s', ptime=%d, ftime=%d WHERE name='%s' AND locale='%s'", $file->header['Last-Translator'], $file->header['Language-Team'], $ptime, $file->time, $file->filename, $lang);
    }
    else {
      db_query("UPDATE {update_locale_file} SET ftime=%d WHERE name='%s' AND locale='%s'", $file->time, $file->filename, $lang);
    }
    if (!db_affected_rows()) {
      db_query("INSERT INTO {update_locale_file} (name, locale, translator, team, ptime, ftime) VALUES ('%s', '%s', '%s', '%s', %d, %d)", $file->filename, $lang, $file->header['Last-Translator'], $file->header['Language-Team'], $ptime, $file->time);
    }
    $fclose = $file->fclose ? $file->fclose : 'fclose';
    $fclose($file->handle);
  }
  if ($file->count[0] || $file->count[1]) {
    watchdog('locale', t('Imported %file into %locale: %number new strings added and %update updated.', array('%file' => $file->filename, '%locale' => $lang, '%number' => $file->count[0], '%update' => $file->count[1])));
  }
  return $file->count;
}

function _update_locale_parse_po(&$file, $lang) {
  $context = '#';
  $current = array();   // Current entry being read
  $plural = 0;          // Current plural form
  $lineno = 0;          // Current line

  $fgets = $file->fgets ? $file->fgets : 'fgets';
  $line = $fgets($file->handle, 10240);
  for (; $line !== FALSE; $line = $fgets($file->handle, 10240)) {
    $lineno++;
    $line = trim(strtr($line, array("\\\n" => '')));
    if ($line == '') continue;

    if ($line[0] == '#') { // A comment
      if ($context == 'msgid' || $context == 'msgid_plural') {
        _locale_import_message('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $file, $lineno);
        return FALSE;
      }
      if ($context != '#') {
        _update_locale_db($current, $file, $lang);
        $current = array();
        $context = '#';
      }
      $current['#'][] = substr($line, 1);
      continue;
    }
    if ($line[0] != '"') { // New entry
      list($st, $line) = explode(' ', $line, 2);
      $line = trim($line);
      if ($st == 'msgid') {
        if ($context == 'msgid' || $context == 'msgid_plural') {
          // Already in this context? Parse error
          _locale_import_message('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $file, $lineno);
          return FALSE;
        }
        if ($context != '#') {
          _update_locale_db($current, $file, $lang);
          $current = array();
        }
        $plural = 0;
      }
      elseif ($st == 'msgid_plural') {
        if ($context != 'msgid') { // Must be plural form for current entry
          _locale_import_message('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $file, $lineno);
          return FALSE;
        }
        $current['msgid'] .= "\0";
      }
      elseif ($st == 'msgstr') {
        if ($context != 'msgid') {   // Should come just after a msgid block
          _locale_import_message('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $file, $lineno);
          return FALSE;
        }
      }
      elseif (strncmp('msgstr[', $st, 7) == 0 && $st[strlen($st) - 1] == ']') {
        if ($context == '#' || $context == 'msgid' || $context == 'msgstr') {
          // Must come after msgid_plural or msgstr[]
          _locale_import_message('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $file, $lineno);
          return FALSE;
        }
        $plural = substr($st, 7, strlen($st) - 8);
      }
      else {
        $st = FALSE;
      }
      $context = $st;
    }
    // String
    $line = _locale_import_parse_quoted($line);
    if ($context == FALSE || $line === FALSE) {
      _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file,  $lineno);
      return FALSE;
    }
    if ($context == '#') {
      _locale_import_message('The translation file %filename contains an error: there is an unexpected string on line %line.', $file, $lineno);
      return FALSE;
    }
    if ($context == 'msgid' || $context == 'msgid_plural') {
      $current['msgid'] .= $line;
    }
    else {
      $current['msgstr'][$plural] .= $line;
    }
  }
  if ($context != '#') { // End of PO file, flush last entry
    if ($context == 'msgid' || $context == 'msgid_plural') {
      _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
      return FALSE;
    }
    _update_locale_db($current, $file, $lang);
  }
  return TRUE;
}

function _update_locale_db($msg, &$file, $lang) {
  if ($msg['msgid'] == '') {
    if (!$file->header && isset($msg['msgstr'][0])) {
      $file->header = _locale_import_parse_header($msg['msgstr'][0]);
    }
    return;
  }
  $english = explode("\0", $msg['msgid'], 2);
  if (isset($english[1])) { // Handle a translation for some plural string
    $entries = array_keys($msg['msgstr']);
    if (count($entries) > 2) {
      $english = array_merge($english, array_fill(0, count($entries) - 2, $english[1]));
    }
    $english = array_map('_locale_import_append_plural', $english, $entries);
    $msg['msgstr'] = array_map('_locale_import_append_plural', $msg['msgstr'], $entries);
  }
  $plid = 0;
  foreach ($msg['msgstr'] as $key => $trans) {
    $comment = _locale_import_shorten_comments($msg['#']);
    $tab = db_fetch_object(db_query("SELECT s.*, t.translation FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid=t.lid AND t.locale='%s' WHERE s.source='%s'", $lang, $english[$key]));
    if ($lid = $tab->lid) {
      if (!$plid) {
        db_query("UPDATE {locales_source} SET location='%s' WHERE lid=%d", $comment, $lid);
      }
      if (!isset($tab->translation)) {
        db_query("INSERT INTO {locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $lang, $trans, $plid, $key);
        ++$file->count[0];
      }
      elseif ($tab->translation != $trans) {
        if ($tab->translation != '' && variable_get('update_locale_moderation', 1)) {
          db_query("UPDATE {update_locale_string} SET translation='%s' WHERE lid=%d AND locale='%s'", $trans, $lid, $lang);
          if (!db_affected_rows()) {
            db_query("INSERT INTO {update_locale_string} (lid, locale, translation) VALUES (%d, '%s', '%s')", $lid, $lang, $trans);
          }
        }
        else {
          db_query("UPDATE {locales_target} SET translation='%s' WHERE lid=%d AND locale='%s'", $trans, $lid, $lang);
        }
        ++$file->count[$tab->translation != '' ? 1 : 0];
      }
      else {
        db_query("DELETE FROM {update_locale_string} WHERE lid=%d AND locale='%s'", $lid, $lang);
        if (db_affected_rows()) {
          ++$file->count[1];
        }
      }
    }
    else {
      db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", $comment, $english[$key]);
      if ($lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE source='%s'", $english[$key]))) {
        db_query("INSERT INTO {locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $lang, $trans, $plid, $key);
        ++$file->count[0];
      }
    }
    $plid = $lid;
  }
}

function _update_locale_install_modules($form_id, $form_values) {
  if ($dep = system_module_build_dependencies($form_values['validation_modules'], $form_values)) {
    foreach (array_keys($dep) as $name) {
      $form_values['status'][$name] = 0;
    }
  }
  $enabled = array();
  foreach ($form_values['status'] as $key => $choice) {
    if ($choice && !module_exists($key)) {
      $enabled[] = $key;
    }
  }
  if ($enabled) {
    _update_locale_import_translations($enabled, FALSE);
  }
}
