Už delší dobu používám k práci webový prohlížeč Chrome (přesněji řečeno Chromium). Jsem s ním relativně spokojen, a osobně jej považuji za nejlepší webový prohlížeč, nicméně i on má nedostatky, a za hlavní nedostatek považuji FTP klienta. V tomto příspěvku popíšu, jak jsem se s tím vypořádal, a dám návod i pro Vás.

Výchozí stav

Výchozí FTP klient, který je v prohlížeči Chromium, funguje pouze ke stahování souborů. Ke své práci však často potřebuji soubory na FTP i nahrávat (uploadovat). Z tohoto důvodu jsem pro práci s FTP stále používal Firefox, a musím přiznat, že kombinování dvou prohlížečů je docela protivné. Proto jsem se rozhodl vymyslet, jak se připojovat k FTP serverům přes Chrome.

FireFTP je vynikající plugin pro Firefox, svého času jsem dokonce jeho autorovi pomáhal s vývojem a českou lokalizací aplikace, a zastáncům Firefoxu ho můžu doporučit. FireFTP slouží jako plnohodnotný FTP klient, který běží uvnitř prostředí prohlížeče Firefox, v samostatné záložce prohlížeče. Klíčovou vlastností pro mě je spuštění FTP klienta při kliknutí na ftp:// odkaz v prohlížeči.

Zvažované alternativy řešení

Mým cílem tedy bylo zprovoznit v Chrome něco podobného, tedy aby se mi kliknutím na ftp:// odkaz ihned otevřel FTP klient. Popíšu úvahy, které se postupně honily v mé hlavě…

Ze začátku jsem si myslel, že bude jednoduché přepsat FireFTP tak, aby fungovalo jako rozšíření pro Chrome. Kdo trochu zná Chrome, ten ví, že rozšíření fungují v podstatě jako obyčejné HTML stránky, programují se v JavaScriptu, rozšířeném o několik funkcí API, které poskytuje Chrome. Naproti tomu FireFTP je psáno v mixu několika jazyků, částečně i v nativním kódu pro jednotlivé platformy (Windows, Linux, MacOS), takže přepsat by se musel komplet celý kód, a toto řešení jsem zavrhl.

Další řešení, které mě napadlo, bylo napsat FTP klienta v JavaScriptu od základu, případně využít jiného JavaScriptového FTP klienta, bohužel se mi nepodařilo najít nic, co by dokázalo přistupovat k FTP serveru a lokálnímu disku zároveň, a přenášet mezi nimi data.

Pak se mé myšlenky obrátily k Java appletům, a myslel jsem, že půjde napsat rozšíření Chrome, které spustí Java applet, který bude sloužit jako FTP klient. Po několika experimentech se mi sice podařilo podobný koncept realizovat, nicméně hlavním problémem je omezení chrome, které nedovoluje spouštět applety, které jsou lokálně uloženy v rozšíření. Výsledný program by tedy musel applety spouštět vzdáleně z nějakého webového umístění, což by sice bylo realizovatelné, ale i tak docela pracné, a proto jsem se rozhodl toto řešení nerealizovat.

Poslední řešení, které mě napadlo, a které jsem nakonec realizoval, je o něco složitější, ale má výhodu v tom, že jsem nemusel psát FTP klienta v některém z jazyků typu JavaScriptu, ale lze využít libovolného nativního klienta v daném počítači (testováno na gFTP v Linuxu). Bohužel Chrome (ani JavaScript) neumí spouštět na Linuxu programy přímo (na Windows by to teoreticky mohlo jít pomocí ActiveX, ale mě šlo především o Linuxové, potencionálně univerzální řešení).

Výsledné řešení

Moje výsledné řešení má tedy následující architekturu:
1. rozšíření pro Chrome, které nahradí všechny ftp:// odkazy za asynchronní volání URL adresy (HTTP request) s parametrem spouštěného příkazu.
2. spouštěcí server, který naslouchá HTTP připojením a spouští příkazy podle instrukcí předávaných v URL
3. libovolný FTP klient, který umožňuje okamžíté připojení předáním ftp:// odkazu jako parametru (já jsem testoval s gFTP)

Rozšíření pro Chrome

Nyní se podrobněji podíváme na první dvě části, a začneme rozšířením Chrome. Pokud jste už nějaké rozšíření psali, tak budete vědět, o čem mluvím, jinak Vás odkážu na samostudium dokumentace.

Mě stačilo vytvořit jednoduché rozšíření, které na všech stránkách zamění ftp:// odkazy za asynchronní HTTP requesty. K tomu jsem využil knihovny jQuery.

Chcete-li si rozšíření připravit sami, vytvořte si složku, do ní vložte soubor soubor manifest.json, do něj napište toto:

  1. {
  2.  "name": "ChromiumFTP",
  3.  "version": "0.1",
  4.  "description": "Extension for running FTP client from web links",
  5.  "content_scripts": [{
  6.   "matches": ["http://*/*"],
  7.   "js": ["jquery.js", "script.js"],
  8.   "all_frames": true
  9.  }]
  10. }

dále do složky nakopírujte distribuci jQuery (jquery.js) a vložte soubor script.js (případně v něm změňte spouštěný příkaz):

  1. $('a').click(function() {
  2.  if ($(this).attr('href').indexOf('ftp://') == 0) {
  3.   $.get('http://localhost:4444/command=gftp%20' + $(this).attr('href'));
  4.   return false;
  5.  }
  6. });

Předpokládá se, že na lokálním počítači poběží spouštěcí server a bude naslouchat na portu 4444. Tomuto skriptu můžete předat libovolný příkaz jako parametr a on jej spustí.

Toto rozšíření nahrajte do Chrome, pokud aktivujete vývojářský mód, nemusíte rozšíření nijak komprimovat, podepisovat, nebo dále upravovat.

Spouštěcí server

Pokud jde o spouštěcí server, ten jsem se rozhodl napsat v Javě, a skládá se ze dvou tříd. První z nich je RunServer, a ta se stará o otevření socketu, a naslouchání příchozím spojením, a spouští instanci druhé třídy:

  1. import java.io.*;
  2. import java.net.*;
  3.  
  4. public class RunServer {
  5.  
  6.  public static void main(String[] args) {
  7.   ServerSocket serverSocket = null;
  8.   boolean listening = true;
  9.  
  10.   try {
  11.    serverSocket = new ServerSocket(4444);
  12.    while (listening) {
  13.     new RunServerThread(serverSocket.accept()).start();
  14.    }
  15.    serverSocket.close();
  16.  
  17.   } catch (Exception e) {
  18.    e.printStackTrace();
  19.   }
  20.  }
  21.  
  22. }

RunServerThread obslouží HTTP spojení (byť velmi jednoduchým způsobem) a spustí požadovaný příkaz:

  1. import java.io.*;
  2. import java.net.*;
  3.  
  4. public class RunServerThread extends Thread {
  5.  
  6.  private Socket socket = null;
  7.  
  8.  public RunServerThread(Socket socket) {
  9.   super("RunServerThread");
  10.   this.socket = socket;
  11.  }
  12.  
  13.  public void run() {
  14.   try {
  15.    PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
  16.    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  17.  
  18.    String inputLine = in.readLine();
  19.    String command = URLDecoder.decode(inputLine.substring(inputLine.indexOf("=")+1, inputLine.lastIndexOf(" ")));
  20.  
  21.    if (command.matches("^gftp\\ (ftp|ftps):\\/\\/([\\w\\.\\\\+]+:{0,1}[\\w\\.\\\\+]*@)?([A-Za-z0-9\\\\.]+)(:[0-9]+)?(\\/|\\/([\\w#!:\\.\\?\\+=&%@!\\\\/\\(\\)]+))?$")) {
  22.     Runtime.getRuntime().exec(command);
  23.    }
  24.  
  25.    out.close();
  26.    in.close();
  27.    socket.close();
  28.  
  29.   } catch (IOException e) {
  30.    e.printStackTrace();
  31.   }
  32.  }
  33. }

Takovýto „server“ je potencionálně relativně všemocný, protože Vám umožní z prohlížeče spustit téměř cokoli, nicméně je díky tomu také docela nebezpečný. Útočník může totiž „zavolat“ příslušnou adresu ze svého počítače s libovolným příkazem, který se spustí ve Vašem počítači a s právy, které má uživatel, pod kterým běží RunServer. Případně Vám může zlou adresu podstrčit, a vy si na ni kliknete sami.

Proto Vám důrazně doporučuji ve firewallu zablokovat příchozí spojení na daný port, neklikat na cizí odkazy, ale hlavně a nejlépe kontrolovat v RunServeru před spouštěním všechny příkazy (např. oproti reg. výrazu, jak jsem to udělal já), a nespouštět nic jiného, než FTP server!

RunServer zkompilujte pomocí javac, spouštějte při startu počítače. Pak do Chrome nainstalujte rozšíření, a potom už se vám při kliknutí na ftp:// odkaz automaticky spustí FTP klient (byť ne v záložce prohlížeči, jako v případě FireFTP, ale to může být na druhou stranu i výhoda).

Závěry

Uznávám, že toto řešení není nejlepší, hlavně co se týče vzdáleného volání by asi bylo korektnější využít SOAP rozhraní, než obyčejnou, a ještě ne úplně korektní HTTP komunikaci, nicméně mě to takto dostačuje.

Pokud máte podobné potřeby jako já, můžete si stáhnout zdrojové kódy.

« »