/** Saves a playlist. */
  public void exportM3U(Playlist playlist) {

    if (playlist == null) {
      return;
    }

    String suggestedName = CommonUtils.convertFileName(playlist.getName());

    // get the user to select a new one.... avoid FrostWire installation folder.
    File suggested;
    File suggestedDirectory = FileChooserHandler.getLastInputDirectory();
    if (suggestedDirectory.equals(CommonUtils.getCurrentDirectory())) {
      suggestedDirectory = new File(CommonUtils.getUserHomeDir(), "Desktop");
    }

    suggested = new File(suggestedDirectory, suggestedName + ".m3u");

    File selFile =
        FileChooserHandler.getSaveAsFile(
            GUIMediator.getAppFrame(),
            I18nMarker.marktr("Save Playlist As"),
            suggested,
            new PlaylistListFileFilter());

    // didn't select a file?  nothing we can do.
    if (selFile == null) {
      return;
    }

    // if the file already exists and not the one just opened, ask if it should be
    //  overwritten.
    // TODO: this should be handled in the jfilechooser
    if (selFile.exists()) {
      DialogOption choice =
          GUIMediator.showYesNoMessage(
              I18n.tr(
                  "Warning: a file with the name {0} already exists in the folder. Overwrite this file?",
                  selFile.getName()),
              QuestionsHandler.PLAYLIST_OVERWRITE_OK,
              DialogOption.NO);
      if (choice != DialogOption.YES) return;
    }

    String path = selFile.getPath();
    try {
      path = FileUtils.getCanonicalPath(selFile);
    } catch (IOException ignored) {
      // LOG.warn("unable to get canonical path for file: " + selFile, ignored);
    }
    // force m3u on the end.
    if (!path.toLowerCase().endsWith(".m3u")) path += ".m3u";

    // create a new thread to handle saving the playlist to disk
    saveM3U(playlist, path);
  }
  /**
   * Handle a Magnet request via a socket (for TCP handling). Deiconify the application, fire MAGNET
   * request and return true as a sign that LimeWire is running.
   */
  public void fireControlThread(Socket socket, boolean magnet) {
    LOG.trace("enter fireControl");

    Thread.currentThread().setName("IncomingControlThread");
    try {
      // Only allow control from localhost
      if (!NetworkUtils.isLocalHost(socket)) {
        if (LOG.isWarnEnabled())
          LOG.warn("Invalid control request from: " + socket.getInetAddress().getHostAddress());
        return;
      }

      // First read extra parameter
      socket.setSoTimeout(Constants.TIMEOUT);
      ByteReader br = new ByteReader(socket.getInputStream());
      // read the first line. if null, throw an exception
      String line = br.readLine();
      socket.setSoTimeout(0);

      BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
      String s = CommonUtils.getUserName() + "\r\n";
      // system internal, so use system encoding
      byte[] bytes = s.getBytes();
      out.write(bytes);
      out.flush();
      if (magnet) handleMagnetRequest(line);
      else handleTorrentRequest(line);
    } catch (IOException e) {
      LOG.warn("Exception while responding to control request", e);
    } finally {
      IOUtils.close(socket);
    }
  }
  /** Loads a playlist. */
  public void importM3U(Playlist playlist) {
    File parentFile = FileChooserHandler.getLastInputDirectory();

    if (parentFile == null) parentFile = CommonUtils.getCurrentDirectory();

    final File selFile =
        FileChooserHandler.getInputFile(
            GUIMediator.getAppFrame(),
            I18nMarker.marktr("Open Playlist (.m3u)"),
            parentFile,
            new PlaylistListFileFilter());

    // nothing selected? exit.
    if (selFile == null || !selFile.isFile()) return;

    String path = selFile.getPath();
    try {
      path = FileUtils.getCanonicalPath(selFile);
    } catch (IOException ignored) {
      // LOG.warn("unable to get canonical path for file: " + selFile, ignored);
    }

    // create a new thread off of the event queue to process reading the files from
    //  disk
    loadM3U(playlist, selFile, path);
  }
  @Override
  public void setUp() throws Exception {
    Expand.expandFile(
        TestUtils.getResourceFile("com/limegroup/gnutella/xml/xml.war"),
        CommonUtils.getUserSettingsDir());

    Injector injector = LimeTestUtils.createInjector();
    limeXMLSchemaRepository = injector.getInstance(LimeXMLSchemaRepository.class);
  }
  /**
   * @return calculates a random delay after the timestamp, unless the timestamp is more than 3 days
   *     in the future.
   */
  private static long delay(long now, long timestamp) {
    if (timestamp - now > THREE_DAYS) return 0;

    long delay = UpdateSettings.UPDATE_DELAY.getValue();
    long random = Math.abs(new Random().nextLong() % delay);
    long then = timestamp + random;

    if (LOG.isInfoEnabled()) {
      LOG.info(
          "Delaying Update."
              + "\nNow    : "
              + now
              + " ("
              + new Date(now)
              + ")"
              + "\nStamp  : "
              + timestamp
              + " ("
              + new Date(timestamp)
              + ")"
              + "\nDelay  : "
              + delay
              + " ("
              + CommonUtils.seconds2time(delay / 1000)
              + ")"
              + "\nRandom : "
              + random
              + " ("
              + CommonUtils.seconds2time(random / 1000)
              + ")"
              + "\nThen   : "
              + then
              + " ("
              + new Date(then)
              + ")"
              + "\nDiff   : "
              + (then - now)
              + " ("
              + CommonUtils.seconds2time((then - now) / 1000)
              + ")");
    }

    return Math.max(0, then - now);
  }
  @Override
  public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

    if (value != null && value instanceof Long) {
      setText(CommonUtils.seconds2time((Long) value));
    } else {
      setText("");
    }
    return this;
  }
 /**
  * Sets the given value for the supplied property key. Nothing is set if the value is empty or
  * null. If the supplied value is a String, it is passed through the I18NConvert.compose method
  * before being set.
  */
 public static void set(Map<FilePropertyKey, Object> map, FilePropertyKey property, Object value) {
   // Insert nothing if value is null|empty.
   if (value != null && !value.toString().isEmpty()) {
     if (value instanceof String) {
       if (FilePropertyKey.isLong(property)) {
         value = CommonUtils.parseLongNoException((String) value);
       } else {
         value = I18NConvert.instance().compose((String) value).intern();
       }
     }
     map.put(property, value);
   }
 }
  /** Constructs an IPFilter that automatically loads the content. */
  @Inject
  public LocalIPFilter(
      @Named("hostileFilter") IPFilter hostileNetworkFilter,
      @Named("backgroundExecutor") ScheduledExecutorService ipLoader) {
    this.hostileNetworkFilter = hostileNetworkFilter;
    this.ipLoader = ipLoader;

    File hostiles = new File(CommonUtils.getUserSettingsDir(), "hostiles.txt");
    shouldLoadHostiles = hostiles.exists();

    hostileNetworkFilter.refreshHosts();
    refreshHosts();
  }
  /** Does the work of setting new good & bad hosts. */
  private void refreshHostsImpl() {
    LOG.debug("refreshing hosts");

    // Load the local blacklist, stripping out invalid entries
    IPList newBad = new IPList();
    String[] allHosts = FilterSettings.BLACK_LISTED_IP_ADDRESSES.get();
    ArrayList<String> valid = new ArrayList<String>(allHosts.length);
    for (int i = 0; i < allHosts.length; i++) {
      if (newBad.add(allHosts[i])) valid.add(allHosts[i]);
    }
    if (valid.size() != allHosts.length) {
      allHosts = valid.toArray(new String[0]);
      FilterSettings.BLACK_LISTED_IP_ADDRESSES.set(allHosts);
    }

    // Load the local whitelist, stripping out invalid entries
    IPList newGood = new IPList();
    allHosts = FilterSettings.WHITE_LISTED_IP_ADDRESSES.get();
    valid = new ArrayList<String>(allHosts.length);
    for (int i = 0; i < allHosts.length; i++) {
      if (newGood.add(allHosts[i])) valid.add(allHosts[i]);
    }
    if (valid.size() != allHosts.length) {
      allHosts = valid.toArray(new String[0]);
      FilterSettings.WHITE_LISTED_IP_ADDRESSES.set(allHosts);
    }

    // Load data from hostiles.txt (if it wasn't already loaded!)...
    if (shouldLoadHostiles) {
      shouldLoadHostiles = false;

      LOG.debug("loading hostiles");
      File hostiles = new File(CommonUtils.getUserSettingsDir(), "hostiles.txt");
      BufferedReader reader = null;
      try {
        reader = new BufferedReader(new FileReader(hostiles));
        String read = null;
        while ((read = reader.readLine()) != null) {
          hostilesTXTHosts.add(read);
        }
      } catch (IOException ignored) {
        LOG.debug("iox loading hostiles", ignored);
      } finally {
        IOUtils.close(reader);
      }
    }

    badHosts = new MultiIPList(newBad, hostilesTXTHosts);
    goodHosts = newGood;
  }
  private void storeAndUpdate(byte[] data, SimppParser parser, UpdateType updateType) {
    if (LOG.isTraceEnabled())
      LOG.trace("Retrieved new data from: " + updateType + ", storing & updating");
    if (parser.getVersion() == IGNORE_ID && updateType == UpdateType.FROM_NETWORK)
      throw new IllegalStateException("shouldn't be here!");

    if (updateType == UpdateType.FROM_NETWORK && httpRequestControl.isRequestPending()) return;

    _lastId = parser.getVersion();
    _lastBytes = data;

    if (updateType != UpdateType.FROM_DISK) {
      FileUtils.verySafeSave(CommonUtils.getUserSettingsDir(), FILENAME, data);
    }

    for (SimppSettingsManager ssm : simppSettingsManagers)
      ssm.updateSimppSettings(parser.getPropsData());
    for (SimppListener listener : listeners) listener.simppUpdated(_lastId);
  }
  public static void populateProperties(
      String fileName,
      long fileSize,
      long creationTime,
      Map<FilePropertyKey, Object> properties,
      LimeXMLDocument doc) {

    properties.put(FilePropertyKey.NAME, ""); // Make sure name defaults to empty.
    set(properties, FilePropertyKey.NAME, FileUtils.getFilenameNoExtension(fileName));
    set(properties, FilePropertyKey.DATE_CREATED, creationTime);
    set(properties, FilePropertyKey.FILE_SIZE, fileSize);

    String extension = FileUtils.getFileExtension(fileName);
    Category category = CategoryConverter.categoryForExtension(extension);

    if (doc != null) {
      for (FilePropertyKey filePropertyKey : FilePropertyKey.values()) {
        set(properties, doc, category, filePropertyKey);
      }

      Long bitrate, length;
      Integer quality;
      switch (category) {
        case AUDIO:
          bitrate = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.AUDIO_BITRATE));
          length = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.AUDIO_SECONDS));
          quality = toAudioQualityScore(extension, fileSize, bitrate, length);
          set(properties, FilePropertyKey.QUALITY, quality);
          break;
        case VIDEO:
          bitrate = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.VIDEO_BITRATE));
          length = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.VIDEO_LENGTH));
          Long height = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.VIDEO_HEIGHT));
          Long width = CommonUtils.parseLongNoException(doc.getValue(LimeXMLNames.VIDEO_WIDTH));
          quality = toVideoQualityScore(extension, fileSize, bitrate, length, height, width);
          set(properties, FilePropertyKey.QUALITY, quality);
          break;
      }
    }
  }
 private File getPropertiesFile() {
   return new File(CommonUtils.getUserSettingsDir(), "intent.props");
 }
  /**
   * Constructs the file system using the given BTData & hash information. If any of the information
   * is malformed, throws a ValueException.
   *
   * @param data contains all the data about a .torrent file
   * @param numHashes number of pieces the torrent was divided into
   * @param pieceLength size of divided up torrent file
   * @param infoHash a string of alphanumeric characters in the .torrent file that the client uses
   *     to verify the data that is being transferred
   * @throws ValueException
   */
  TorrentFileSystem(BTData data, int numHashes, long pieceLength, byte[] infoHash)
      throws IOException {
    // name of the torrent, also specifying the directory under which to save the torrents.
    _name = CommonUtils.convertFileName(data.getName());

    // we need to check the name of the torrent, security risk!
    if (_name.length() == 0) throw new ValueException("bad torrent name");

    _incompleteFile =
        new File(
            SharingSettings.INCOMPLETE_DIRECTORY.get(),
            Base32.encode(infoHash) + File.separator + _name);
    _completeFile = new File(SharingSettings.getSaveDirectory(_name), _name);

    if (!FileUtils.isReallyParent(SharingSettings.getSaveDirectory(_name), _completeFile)) {
      throw new SaveLocationException(LocationCode.SECURITY_VIOLATION, _completeFile);
    }

    if (data.getFiles() != null) {
      List<BTData.BTFileData> files = data.getFiles();
      List<TorrentFile> torrents = new ArrayList<TorrentFile>(files.size());
      long position = 0;
      for (BTData.BTFileData file : files) {
        String torrentPath = _name + file.getPath();
        TorrentFile f =
            new TorrentFile(
                file.getLength(),
                new File(_completeFile, file.getPath()).getAbsolutePath(),
                torrentPath);
        f.setBeginPiece((int) (position / pieceLength));
        f.setStartByte(position);
        position += f.length();
        f.setEndPiece((int) (position / pieceLength));
        f.setEndByte(position - 1);

        if (!FileUtils.isReallyInParentPath(_completeFile, f))
          throw new SaveLocationException(LocationCode.SECURITY_VIOLATION, f);
        torrents.add(f);
      }

      if (files.size() == 0) throw new ValueException("bad metainfo, no files!");

      _files = torrents;

      // add folders, for easier conflict checking later on
      for (String folderPath : data.getFolders()) _folders.add(new File(_completeFile, folderPath));
      _folders.add(_completeFile);
    } else {
      String torrentPath = data.getName();
      TorrentFile f =
          new TorrentFile(data.getLength(), _completeFile.getAbsolutePath(), torrentPath);
      f.setBeginPiece(0);
      f.setStartByte(0);
      f.setEndPiece(numHashes - 1);
      f.setEndByte(f.length());
      _files = new ArrayList<TorrentFile>(1);
      _files.add(f);
    }

    _unmodFiles = Collections.unmodifiableList(_files);
    _totalSize = calculateTotalSize(_files);
    if (_totalSize <= 0) throw new ValueException("invalid size " + _totalSize);
  }
    private void copyPlaylistFilesToFolder(Playlist playlist) {
      if (playlist == null || playlist.getItems().isEmpty()) {
        return;
      }

      File suggestedDirectory = FileChooserHandler.getLastInputDirectory();
      if (suggestedDirectory.equals(CommonUtils.getCurrentDirectory())) {
        suggestedDirectory = new File(CommonUtils.getUserHomeDir(), "Desktop");
      }

      final File selFolder =
          FileChooserHandler.getSaveAsDir(
              GUIMediator.getAppFrame(),
              I18nMarker.marktr("Where do you want the playlist files copied to?"),
              suggestedDirectory);

      if (selFolder == null) {
        return;
      }

      // let's make a copy of the list in case the playlist will be modified during the copying.
      final List<PlaylistItem> playlistItems = new ArrayList<>(playlist.getItems());

      BackgroundExecutorService.schedule(
          new Thread("Library-copy-playlist-files") {
            @Override
            public void run() {

              int n = 0;
              int total = playlistItems.size();
              String targetName = selFolder.getName();

              for (PlaylistItem item : playlistItems) {
                File f = new File(item.getFilePath());
                if (f.isFile() && f.exists() && f.canRead()) {
                  try {
                    Path source = f.toPath();
                    Path target =
                        FileSystems.getDefault().getPath(selFolder.getAbsolutePath(), f.getName());
                    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
                    n++;

                    // invoked on UI thread later
                    String status = String.format("Copied %d of %d to %s", n, total, targetName);
                    LibraryMediator.instance().getLibrarySearch().pushStatus(status);
                  } catch (IOException e) {
                    e.printStackTrace();
                  }
                }
              }

              GUIMediator.launchExplorer(selFolder);

              // and clear the output
              try {
                Thread.sleep(2000);
                LibraryMediator.instance().getLibrarySearch().pushStatus("");
              } catch (InterruptedException e) {
              }
            }
          });
    }
  /**
   * Stores the given data to disk & posts an update to neighboring connections. Starts the download
   * of any updates
   */
  private void storeAndUpdate(byte[] data, UpdateCollection uc, UpdateType updateType) {
    if (LOG.isTraceEnabled())
      LOG.trace("Retrieved new data from: " + updateType + ", storing & updating.");
    if (uc.getId() == IGNORE_ID && updateType == UpdateType.FROM_NETWORK)
      throw new IllegalStateException("shouldn't be here!");

    // If an http max request is pending, don't even bother with this stuff.
    // We want to get it straight from the source...
    if (updateType == UpdateType.FROM_NETWORK
        && httpRequestControl.isRequestPending()
        && httpRequestControl.getRequestReason() == HttpRequestControl.RequestReason.MAX) return;

    _lastId = uc.getId();

    _lastTimestamp = uc.getTimestamp();
    UpdateSettings.LAST_UPDATE_TIMESTAMP.setValue(_lastTimestamp);

    long delay = UpdateSettings.UPDATE_DOWNLOAD_DELAY.getValue();
    long random = Math.abs(RANDOM.nextLong() % delay);
    _nextDownloadTime = _lastTimestamp + random;

    _lastBytes = data;

    if (updateType != UpdateType.FROM_DISK) {
      // cancel any http and pretend we just updated.
      if (httpRequestControl.getRequestReason() == HttpRequestControl.RequestReason.TIMEOUT)
        httpRequestControl.cancelRequest();
      UpdateSettings.LAST_HTTP_FAILOVER.setValue(clock.now());

      FileUtils.verySafeSave(CommonUtils.getUserSettingsDir(), FILENAME, data);
      capabilitiesVMFactory.updateCapabilities();
      connectionManager.get().sendUpdatedCapabilities();
    }

    Version limeV;
    try {
      limeV = new Version(LimeWireUtils.getLimeWireVersion());
    } catch (VersionFormatException vfe) {
      LOG.warn("Invalid LimeWire version", vfe);
      return;
    }

    Version javaV = null;
    try {
      javaV = new Version(VersionUtils.getJavaVersion());
    } catch (VersionFormatException vfe) {
      LOG.warn("Invalid java version", vfe);
    }

    // don't allow someone to set the style to be above major.
    int style = Math.min(UpdateStyle.STYLE_MAJOR, UpdateSettings.UPDATE_STYLE.getValue());

    UpdateData updateInfo =
        uc.getUpdateDataFor(
            limeV, ApplicationSettings.getLanguage(), LimeWireUtils.isPro(), style, javaV);

    List<DownloadInformation> updatesToDownload = uc.getUpdatesWithDownloadInformation();
    _killingObsoleteNecessary = true;

    // if we have an update for our machine, prepare the command line
    // and move our update to the front of the list of updates
    if (updateInfo != null && updateInfo.getUpdateURN() != null) {
      prepareUpdateCommand(updateInfo);
      updatesToDownload = new LinkedList<DownloadInformation>(updatesToDownload);
      updatesToDownload.add(0, updateInfo);
    }

    _updateInfo = updateInfo;
    _updatesToDownload = updatesToDownload;

    downloadUpdates(updatesToDownload, null);
    if (updateInfo == null) {
      LOG.warn("No relevant update info to notify about.");
      return;
    } else if (updateInfo.getUpdateURN() == null || isHopeless(updateInfo)) {
      if (LOG.isDebugEnabled())
        LOG.debug(
            "we have an update, but it doesn't need a download.  "
                + "or all our updates are hopeles. Scheduling URL notification...");

      updateInfo.setUpdateCommand(null);

      backgroundExecutor.schedule(
          new NotificationFailover(_lastId),
          delay(clock.now(), uc.getTimestamp()),
          TimeUnit.MILLISECONDS);
    } else if (isMyUpdateDownloaded(updateInfo)) {
      LOG.debug("there is an update for me, but I happen to have it on disk");
      fireUpdate(updateInfo);
    } else LOG.debug("we have an update, it needs a download.  Rely on callbacks");
  }
 /** Simple accessor for the stored file. */
 private File getStoredFile() {
   return new File(CommonUtils.getUserSettingsDir(), FILENAME);
 }
  /**
   * Returns the fully-qualified temporary download file for the given file/location pair. If an
   * incomplete file already exists for this URN, that file is returned. Otherwise, the location of
   * the file is determined by the "incDir" variable. For example, getFile("test.txt", 1999) may
   * return "C:\Program Files\LimeWire\Incomplete\T-1999-Test.txt" if "incDir" is "C:\Program
   * Files\LimeWire\Incomplete". The disk is not modified, except for the file possibly being
   * created.
   *
   * <p>This method gives duplicate files the same temporary file, which is critical for resume and
   * swarmed downloads. That is, for all rfd_i and rfd_j
   *
   * <pre>
   *      similar(rfd_i, rfd_j) <==> getFile(rfd_i).equals(getFile(rfd_j))<p>
   * </pre>
   *
   * It is imperative that the files are compared as in their canonical formats to preserve the
   * integrity of the filesystem. Otherwise, multiple downloads could be downloading to "FILE A",
   * and "file a", although only "file a" exists on disk and is being written to by both.
   *
   * @throws IOException if there was an IOError while determining the file's name.
   */
  public synchronized File getFile(String name, URN sha1, long size, File incDir)
      throws IOException {
    boolean dirsMade = false;
    File baseFile = null;
    File canonFile = null;

    // make sure its created.. (the user might have deleted it)
    dirsMade = incDir.mkdirs();

    String convertedName = CommonUtils.convertFileName(name);

    try {

      if (sha1 != null) {
        File file = hashes.get(sha1);
        if (file != null) {
          // File already allocated for hash
          return file;
        } else {
          // Allocate unique file for hash.  By "unique" we mean not in
          // the value set of HASHES.  Because we allow risky resumes,
          // there's no need to look at BLOCKS as well...
          for (int i = 1; ; i++) {
            file = new File(incDir, tempName(convertedName, size, i));
            baseFile = file;
            file = canonicalize(file);
            canonFile = file;
            if (!hashes.values().contains(file)) break;
          }
          // ...and record the hash for later.
          hashes.put(sha1, file);
          // ...and make sure the file exists on disk, so that
          //   future File.getCanonicalFile calls will match this
          //   file.  This was a problem on OSX, where
          //   File("myfile") and File("MYFILE") aren't equal,
          //   but File("myfile").getCanonicalFile() will only return
          //   a File("MYFILE") if that already existed on disk.
          //   This means that in order for the canonical-checking
          //   within this class to work, the file must exist on disk.
          FileUtils.touch(file);

          return file;
        }
      } else {
        // No hash.
        File f = new File(incDir, tempName(convertedName, size, 0));
        baseFile = f;
        f = canonicalize(f);
        canonFile = f;
        return f;
      }

    } catch (IOException ioe) {
      IOException ioe2 =
          new IOException(
              "dirsMade: "
                  + dirsMade
                  + "\ndirExist: "
                  + incDir.exists()
                  + "\nbaseFile: "
                  + baseFile
                  + "\ncannFile: "
                  + canonFile);
      ioe2.initCause(ioe);
      throw ioe2;
    }
  }
 /**
  * Returns the last directory that was used in a FileChooser.
  *
  * @return
  */
 public static File getLastInputDirectory() {
   File dir = ApplicationSettings.LAST_FILECHOOSER_DIRECTORY.getValue();
   if (dir == null || dir.getPath().equals("") || !dir.exists() || !dir.isDirectory())
     return CommonUtils.getCurrentDirectory();
   else return dir;
 }