ttcopyを使ってホスト間でカジュアルにコピペをしよう

# 端末エミュレータ上でのコピペの不満

リモートのサーバにログインしたあとに、ログやコマンドの実行結果、サーバ上にあるファイルの中身などを、ローカル環境のテキストエディタにコピペしたい時みなさんはどうやっていますか？おそらく、TeraTerm、iTermなどの端末エミュレータの上から直接カーソルでコピペする方が多いんじゃないかと思います。

しかしこの方法だと、コピペする範囲が一画面に収まりきらない時に何かと不便です。特に量が多いとスクロールが面倒ですし、端末エミュレータが保存してくれるスクロールバッファ[^tmux_buf]の容量も無限ではないので、コピペしたかった情報自体が損失してしまうこともしばしばあります。

また、画像や音声などのバイナリのファイルのコピペはできないのも不便です。

[^tmux_buf]: ターミナル上に表示される情報の履歴のこと。例えばTMUXを使うと`history-limit`という変数で制御される。

# ttcopy

そこで [@mecab](https://twitter.com/mecab) さんと一緒に **ttcopy** というコマンドラインツールを作ってみました。これにより前述した問題を解決することができます。

[![Screen Shot 2017-02-27 at 9.46.36 PM.png](https://qiita-image-store.s3.amazonaws.com/0/68963/4111a9f7-fe97-eb66-b1ae-4588f1903082.png)](https://github.com/greymd/ttcopy)

**Github:** https://github.com/greymd/ttcopy

まず、情報を共有したい複数のホストに**ttcopy**をインストール(方法は[後述](#インストール方法))します。このツールに含まれる`ttcopy`と`ttpaste`というコマンドを使うことで、下記のように異なるホスト[^definition_of_host]をまたいで、コピー&ペーストをすることができます。

[^definition_of_host]: 念のため言葉の定義をしておきますが、この記事では基本的に「ネットワーク経由で情報をやり取りできるコンピュータ」という意味で「ホスト」という言葉を使います。コンピュータの規模や用途は考えません。なので、macOSのこともここではホストと呼ばせて下さい。また何らかのサービスを提供している「ホスト」を明示する時は「サーバ」と呼びます。

```bash:ホスト1
# "foobar"という文字列をコピー
$ echo foobar | ttcopy
```

```bash:ホスト2
# ttpasteというコマンドで、コピーした文字列を標準出力に出せる
$ ttpaste
foobar
```

このツールは当然ローカルのマシンとリモートのホスト間でも使えます。このツールは、インターネットに接続しているホスト上で使え、また利用する上で**何かのWebサービスに会員登録する必要はありません。**


# 動作環境

ttcopyは、`bash`か`zsh`が動き、`curl`と`openssl`コマンドが使えるインターネットに接続されたホストであれば動かすことができます[^test_env]。LinuxあるいはmacOSを動作環境として想定しています[^other_platform]。

[^other_platform]: ただしテストはしていませんが、CygwinやBash on Windowsでも動くと思います。
[^test_env]: bashは3.2、4.2で、zshは4.3、5.0で動作確認をしています。

#インストール方法

下記のインストール手順のいずれかを、このツールを使いたいホスト全てに実施します。
なおこの記事では[ttcopy v2.2.0](https://github.com/greymd/ttcopy/releases/tag/v2.2.0)を前提とします。

## [Homebrew](https://github.com/Homebrew/brew)

macOSをお使いの方は`brew`でインストールすることが可能です。

```bash:Terminal
$ brew tap greymd/ttcopy
$ brew install ttcopy
```

## `apt-get`

Ubuntuをお使いの方は、`apt-get` あるいは `apt` を使ってインストールできます[^support]。

[^support]: サポート期限が継続しているバージョンのUbuntuに対応しています。2017年2月現在、Ubuntu12.04(Precise)以降にリリースされたLTSおよび16.10(Yakkety)がサポート対象となります。

```bash:Terminal
# `add-apt-repository` コマンドがない人はインストールして下さい
$ sudo apt-get install software-properties-common

# Ubuntu 12.04 の人は python-software-properties をインストール

$ sudo add-apt-repository ppa:ttcopy/ppa
$ sudo apt-get update
$ sudo apt-get install ttcopy
```

## Zsh ([zplug](https://github.com/zplug/zplug))
Zshユーザの方で[zplug](https://github.com/zplug/zplug) をお使いの方は、下記のように`~/.zshrc`を編集することで手軽にインストールが可能です[^other_zsh_plug]。

[^other_zsh_plug]: [Antigen](http://antigen.sharats.me/)を始めとした他のプラグインマネージャでも使えます。

```bash:~/.zshrc
zplug "greymd/ttcopy"
```

## 手動でインストール

root権限がないなどの事情で上記のいずれの手順も実施できない方は、[Githubにあるリポジトリ](https://github.com/greymd/ttcopy)をクローンしてください。

```bash:Terminal
# 適当なディレクトリにクローン
git clone https://github.com/greymd/ttcopy.git ~/ttpcopy
```

そして下記の一行を`~/.bashrc`あるいは`~/.zshrc`に加えます。`source`で`ttcp_activate.sh`というファイルを読み込んでいます。

```bash
source ~/ttcopy/ttcp_activate.sh
```

## 初期設定

インストールが完了したら、`ttcopy`コマンドを実行してみましょう。すると初期設定画面が現れます。

```bash:Terminal
# 一番最初に実行すると案内画面が現れる
$ ttcopy
Set default ID/Password.
Enter ID for ttcopy/ttpaste:         # <= ここにIDを入力
Enter password for ttcopy/ttpaste:   # <= ここにパスワードを入力
Enter same password again:           # <= ここにパスワードを再入力

Created credential file '/home/user/.config/ttcopy/config'
Execute 'ttcopy --init' to show this screen again.
```

ここで、半角英数字か半角の記号で構成された、適当な長さのIDとパスワードを入力して下さい[^limitation]。

[^limitation]: 特に文字数に制限はありません。なので1文字でもいけますが、8文字以上がいいでしょう。

IDは他人と被ると正しくコピペができない可能性があるので、ある程度複雑なものが良いです。このID自体はそのまま外に漏れることは無いので[^architecture]、e-mailアドレスを設定するのも一つの手です。

`ttcopy`はコピーする内容をWeb上のクリップボードに保存する際に内容を暗号化します。その暗号化 & 復号化に使われるキーがパスワードとなります。心配症の方は、8文字以上かつ大文字と小文字と記号を混ぜるなど、十分に複雑なもののほうが良いでしょう。

上記の手順で、情報共有したいホスト全てに同じIDとパスワードを設定します。パスワードを再設定したいという方は`ttcopy`コマンドを`--init`オプションをつけて再度実行して下さい。

[^architecture]: IDはSHA-256でsalt付きでハッシュ化されます。その値が外部サービスのIDとして使われるので、メールアドレスを設定したとしても特定はされません。https://github.com/greymd/ttcopy/wiki/Security


# 使い方

先ほど設定したIDとパスワードが同じ値を持つホストの間でのみ、情報のやり取りができます。コピーをする際は`ttcopy`というコマンドに標準入力でデータを入力します。ペーストをする際は`ttpaste`というコマンドを打つと、コピーしたデータをそのまま標準出力に出してくれます。

### 連続してペースト

```bash:ホスト1
# コピーする。コピーされると"Copied!"と表示される。
$ seq 10 | head -n 3 | ttcopy
Copied!
```

```bash:ホスト2
# ペーストする
$ ttpaste
1
2
3

# 同じホストであればペーストは何回もできる
$ ttpaste
(Pasting the last paste) # => ここ表示は標準エラー出力です。
1
2
3
```

### バイナリ情報のやりとり

テキストだけでなく、バイナリ情報も扱えます。つまり、**画像や音声など、どんな情報でも扱えます。**

* ファイル転送に使う

```bash:ホスト1
# 画像をパイプで繋いでttcopyに与える
$ cat image.jpg | ttcopy
```

```bash:ホスト2
# 確かに画像情報が出力されている
$ ttpaste | file -
/dev/stdin: JPEG image data, JFIF standard 1.01

# これでホスト1の画像をホスト2に転送できる
$ ttpaste > image.jpg
```

### プロキシサーバを経由する

ファイアウォールで守られているネットワーク内で`ttcopy`を使いたいという方は、`TTCP_PROXY`というシェル変数にインターネットに接続するためのプロキシサーバとポート番号を設定して下さい。すると、`ttcopy`と`ttpaste`はその変数で指定されたプロキシサーバを経由してコピー & ペーストをしてくれます。

```bash:ホスト1
# プロキシ経由でのコピー
$ echo ABCDEFG | TTCP_PROXY="http://example.proxy.server1.com:1234" ttcopy
```

```bash:ホスト2
# プロキシ経由でのペースト
$ TTCP_PROXY="http://example.proxy.server2.com:5678" ttpaste
ABCDEFG
```

常に特定のプロキシサーバを使いたいという方は下記の一行を`~/.bashrc`あるいは`~/.zshrc`に記述し、環境変数として登録してしまえば毎回設定する必要が無いので楽だと思います。

```bash:~/.bashrc(~/.zshrc)
export TTCP_PROXY="http://example.proxy.server1.com:1234"
```

# 仕様

* 最大10GBまでのデータをコピーできます[^spec_transfer]。
* 一回特定の端末でペーストをすると、それ以外の端末では同じIDとパスワードを持っていたとしてもペーストができなくなります。再度コピーして下さい。
* コピーしたデータは一旦transfer.shというサービスに暗号化されたまま保存されますが、14日間で消去されます[^spec_transfer]。
* 必要なWeb上でのリクエストは全てHTTPSを使用、更にデータの内容自体はAES-256-CBCで暗号化されます[^enc_flow]。

[^spec_transfer]: 裏で使っているtransfer.shの仕様です。
[^spec_cl1ip]: 裏で使っているcl1p.netの仕様です。
[^enc_flow]: 暗号化のフローに関しては、GithubのWikiを参照 https://github.com/greymd/ttcopy/wiki/Security


# 注意点

 * 外部依存サービスの障害や万が一のIDの衝突などで「コピーしたつもりなのにペーストできない」といった状況がいつ発生するかわかりません。心配な方は、IDとパスワードを定期的に変更したほうが良いです。また、なにかこのツールで不都合が発生しても作者は一切の責任を負いません。

 * ペーストが完了するまでは、コピー対象の元データは必ず保存しておいて下さい。外部依存サービス側の何らかの障害、利用するIDの衝突などでデータが損失する可能性は0とは言えません。

 * コピーする内容は暗号化しているとはいえ、何があるか分かりません[^akui]。万が一漏れても許容できるレベルの情報のやり取りに使って下さい。クレジットカード番号や個人情報など、あまりにもセンシティブな情報はコピペしないことを推奨します。

[^akui]: 悪意のある他人がペースト内容を差し替えることも万が一ですがありえます。また、IDの衝突攻撃も考えられなくはないです。


# まとめ

ホスト間で気軽にコピペができるttcopyというツールを紹介しました。作者の一人である私も実際によく使います。友達と口頭でIDとパスワードを教え合い、情報をやり取りするときにとても便利です[^ttcopy_options]。
「何かバグを見つけたよ」「こんな機能があったらいいんじゃない？」という方は、Github上でissueを立てていただく、あるいはプルリクエストを作成していただくなど、どうぞお気軽に絡んでください😁

**Github:** https://github.com/greymd/ttcopy

[^ttcopy_options]: `-i`オプションと`-p`オプションを使うと、他人との情報のやり取りをとてもカジュアルにできるのでおすすめです。詳細についてはGithubの[README.md](https://github.com/greymd/ttcopy/blob/master/README.md)を参照です。

# [付録]ttcopyの仕組み

このツールでは無償かつ会員登録せずに使える下記の2種類のWebサービスをマッシュアップさせています。

#### transfer.sh ( https://transfer.sh/ )

コマンドラインでファイルを簡単に共有するためのサービスです。公式サイトのデモにあるように`curl`コマンド一つでファイルをWeb上にアップロードできます。その際に、アップロードしたファイルにアクセスするためのURLを自動生成してくれるので、他人とのファイル共有ができます。ファイル名が被っても良いように、URLにはランダムな文字列が付与されます。このサービスの利点はアップロード可能容量が10GBと大きく、どのような形式のファイルのアップロードにも対応していることです。

#### cl1p.net ( https://cl1p.net/ )

インターネットのクリップボードを謳っているサービスです。任意の固定のサブディレクトリ名を持ったURLに、テキストを紐付けることができます[^attention]。一旦テキストを紐付けたそのURLを参照すると、そのテキストを得ることができます。それと同時にcl1p.net側に保存されていたテキストは消滅します。
このサービスは単体でもそこそこ使えるのですが、保存する情報はテキストのみ、かつ扱える情報量が比較的少ない[^limit_clip]などの欠点があります。ただし、常に固定のURLが使えるという利点があります。

[^attention]: 任意のサブディレクトリ名なので、当然他人と被る可能性があります。
[^limit_clip]: 筆者が試した限りは、600キロバイト程度のテキストを保存しようとしたらcl1p.net側がエラーを返しました。

`ttcopy`は、標準入力の内容を含んだ一時ファイルをtransfer.shにアップロードして、URLを生成します。さらに、その生成されたURLをcl1p.netに貼り付けます。

`ttpaste`はcl1p.netからURLを取得し、そのURLからアップロード済みのファイルの中身を標準出力に吐き出します。

transfer.shとcl1p.netの両者の長所を組み合わせることで、このようなツールが完成しました。さらに詳しく知りたい方は、[Github](https://github.com/greymd/ttcopy)でソースを見ていただく、あるいは[ttcopyのwiki](https://github.com/greymd/ttcopy/wiki)をご参照下さい。