メールが送信されるまでをパケットを見ながら解剖!

以前、コマンドだけでメールを送信する記事を書きましたが、
メール送信の一連の流れをパケットで追いかけていきたいと思います!

人物相関図


summary.jpg

下記のソースを使えば、上記の図の構成でDocker環境を構築することができ、実際に動作を検証することができます。
https://github.com/kuniiskywalker/mail-tester

登場人物


送信君(送り手のメールサーバー)

IP:10.5.0.6
ドメイン:fuga.local


受信君(受け手のメールサーバー)

役割:メールサーバー
IP:10.5.0.5
ドメイン:hoge.local


案内マン(ネームサーバー)

IP:10.5.0.4


あらすじ(送信完了までの送信サーバーのパケットの流れ)

この図を元に解説していきます。

packet-flow.png

① 送信先メールアドレスのMXレコード確認


sender-mx.png

※MXレコード:メールの送信先IPアドレスが設定されたDNSレコード。


 送信君:

root@hoge.localに送信したいので、hoge.localのMXレコード教えてください

 案内マン:
MXレコードはmail.hoge.localです。

 送信君:

ではmail.hoge.localのIPアドレスを教えてください。

 案内マン:

10.5.0.5です。

② TCPハンドシェイク


tcp-handshake.png

 送信君:

はじめまして受信君!
案内マンからIPアドレスきいてご連絡しました!
今からメールを送るためのやり取りしたいのですがいいですか?

 受信君:

はい大丈夫です!

 送信君:

ありがとうございます!

③ EHLO


※EHLOとはSMTP拡張機能を使うために送信者と受信者でお互いにサポートしてる機能をあわせて使用しましょうというやり取り。

sender-ehlo.png

 受信君:

メール受信を行う上での開始準備できてます!

 送信君:

それではまず、受信君はどういった機能をサポートしてるか知りたいので、教えてもらっていいですか?

 受信君:

↓こういうやつに対応してます!

SIZE:受信可能サイズ 10240KB

PIPELINE:SMTPで送信を行うための準備に使うコマンドを一括実行できる

他は割愛。。。


 送信君:

ありがとうございます!

PIPELINE対応してるんですねー、ではまとめて送信メールアドレスとか、送り元メールアドレスを送りますので、送信可能かどうか確認お願いします!

※MAIL FROM, RCTP TO, DATAコマンドを使って上記の確認を行う。

※1個ずつ実行していく場合は通信の利用効率が悪くなるため、こういった方式が導入されている。

⑤ 送信可否結果



 受信君:
結果をまとめてお返しします!


 受信君:
ありがとうございます!
全部OKそうですね!

⑥ メール送信


 送信君:
結果うけとりました!
メールを送信を許可いただきありがとうございます!

それではroot@hoge.local宛てに、差出人root@fuga.localとして、件名:test本文:This is test mail.でお送りします!

imf.png

まとめ

登場人物も含めてやり取りをまとめてみました!

ちょっと駆け足で書いたので、ちょっとずつ修正します。。。

ローカル環境でメールの送受信の確認ができる環境をDockerでつくったのでご活用ください!
https://github.com/kuniiskywalker/mail-tester

Please share  

SNIのメモ

SNIのパケット覗き見

SNI(Server Name Indication)とは?

apacheのTLS通信用バーチャルホストをネームベース(ホスト名)で設定できる技術。
通常TLS通信の場合は、ホスト名も含めて暗号化されるからapacheは対応するバーチャルホストを見つけることができず、IPで見分けるしかなかった。

つまりIPひとつに対して443ポートが一つしか関連付けできないため、
事実上、IPに対してひとつしかTLSのバーチャルホストを設定できなかった。

そこで、ホスト名を暗号せずにブラウザから送信して、ネームベースでバーチャルホストの設定をできるようにする技術をSNI(Server Name Indication)と呼ぶらしい。

TLS拡張(RFC4366)
http://www.ipa.go.jp/security/rfc/RFC4366JA.html

検証したこと

  1. 俺俺TLS証明書
  2. httpd-ssl.confに以下バーチャルホストを設定
  3. chromeで https://test1.localhost にアクセス
  4. 実際パケット上でサーバー名がどうやって送信されるかwiresharkで確認

httpd-ssl.confの設定例

1
2
3
4
5
6
7
<VirtualHost *:443>
ServerName test1.localhost
DocumentRoot "/usr/local/apache2/htdocs"
SSLEngine on
SSLCertificateFile "/usr/local/apache2/conf/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
</VirtualHost>

パケット

TLSハンドシェイクのClient Hello時(Client Helloについてはこちら)にTLS拡張で暗号化せずにserver_nameを送信してる。
おそらくapacheはこの情報をみてバーチャルホストを判定してる。

client_hello.jpg

まとめ

パケットを見て分かるように、クライアントからservernameを暗号せずに送信してるため、当然のごとくクライアント側の実装依存になってしまい、旧ブラウザとか使えない(今はどうか知らない。。。)

apacheの設定をすることもめっきり減りましたが、ちょっとレンタルサーバーいじってて気になったのでメモしてみました(´・ω・`)

Please share  

tlsハンドシェイクのメモ

tlsハンドシェイクとかわけわかめだったのでメモ。

やったこと

1. TLSの俺俺証明書作成

1
2
3
$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt

2. dokcerでapacheたてて、tcpdumpでパケットキャプチャー

apache用dockerコンテナー

https://github.com/kuniiskywalker/docker-test-capture-https

apache設定内容

DH鍵交換(DHE)無効

クライアントとサーバーで一時的な秘密鍵を生成して共通鍵を交換する安全な方法ですが、wiresharkで復号できないためRSAを使った鍵交換のみに設定。
但しクライアント側でもDHEを無効にしないと意味ない。

1
2
# SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLCipherSuite kRSA
SSLのキャッシュ無効

キャッシュを利用されると二回目以降とかの通信でhttpsの通信の一部を省略されてしまうため無効

1
2
3
4
#SSLSessionCache "dbm:/usr/local/apache2/logs/ssl_scache"
#SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCache none
SSLSessionCacheTimeout 300

3. クライアントからcurlでアクセス

ブラウザの設定でDHEを無効にするのが面倒だったので、curlで検証しました。。。

1
2
# dokcerが動いてるvmによって以下アクセス先は要変更
curl --cacert server.crt https://localhost

4. キャプチャしたパケットをwiresharkで眺める

Wiresharkを開き、キャプチャしたファイルを開く。

通信が開始されるまでの大まかな流れ

capture1.jpg

Hello交換

事前にこういう方式で暗号化のやりとりしますよという取り決め

鍵交換

暗号化に使う鍵の交換
(実際はパラメーターを送信しあってお互いに共通の鍵を作る)

データ通信

利用開始!
実際は暗号化されていて中身は暗号化されていて、よくわからない文字列で埋め尽くされている

1.Hello交換

ざっくり言うと私(クライアント)が暗号化方式とか諸々提示しますので、あなた(サーバー)がどうやって暗号化するか決めてください。

というやりとりっぽい。

◯client hello

client_hello.jpg

プロトコルバージョン

TLS1.0, 1.1, 1.2などの接続に用いるプロトコルを提示

(クライアント:TLS1.2で接続でお願いします!)

疑似乱数

クライアント側で生成した乱数をサーバーに送信

(クライアント:後で鍵交換に使う文字列なので控えといてください!)

暗号化方式

候補値(複数可)を提示

(クライアント:暗号化方式をこの中からお選びください!)

圧縮方式

候補値(複数可)を提示

(クライアント:圧縮方式は無圧縮でお願いします!)

※過去に色々問題があったため無圧縮一択らしい

拡張

候補値(複数可)を提示

(クライアント:その他もろもろの拡張に使用するものはこの中からお選びください)

※拡張に関しては割愛します

◯server hello

server_hello.jpg

プロトコルバージョン

クライアントから提示された接続に用いるプロトコルに合意

(サーバー:TLS1.2で接続了解しました!)

疑似乱数

サーバー側で生成した乱数をクライアントに送信

(サーバー:後で鍵交換に使う文字列なので控えといて!)

暗号方式(暗号スイート CipherSuite)

提示された暗号化方式を選択

(サーバー:TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)でOKです)

  • TLS_RSA:サーバーのRSA鍵を使った鍵交換アルゴリズム
    今回は強制的にRSAを使う設定にしたのでこれが選択された模様

  • AES_256_GCM:共通鍵暗号のアルゴリズム
    TODO:後で調べる

  • SHA384:MAC(メッセージ認証)のアルゴリズム
    TODO:後で調べる

圧縮方式

提示された圧縮方式を選択

(サーバー:無圧縮でOKです)

◯Certificate

サーバーの証明書情報をクライアントへ。

certificate.jpg

◯Server Hello Done

Server Hello 終了

2.鍵交換

暗号化する際に使用する鍵を生成する。

◯Client Key Exchange

クライアントで生成したキーをサーバーに送信する。

鍵交換アルゴリズム

1. RSA

クライアント側で生成した共通鍵生成に必要な情報を、サーバー側の公開鍵を使って暗号化して、
サーバーに送信する。
悪い人に暗号化したデータをずっと保存されて、ある日サーバーの秘密鍵が漏洩してすべて解読されてしまうというリスクがある。

ClientKeyExchangeのみ行われる。

client_key_exchange.jpg

上記Certificateでサーバーから取得した認証情報から公開鍵を取り出し、「ランダムな文字列」を暗号化してサーバーに送信する。

以下の部分が暗号化された文字列
EncryptedPreMasterSecret.jpg

このPreMaster Secretの値を使って後述するクライアントとサーバーで使用する共通鍵を作成するっぽい。

2. DHE

サーバーの公開鍵ではなく、お互いに一時的に生成した秘密鍵/公開鍵のペアで公開鍵を送り合って、
共通鍵生成に必要な情報を暗号化して送り合う。
ServerKeyExchangeとClientKeyExchangeの2回の通信で鍵交換を行います

一時的に作成される鍵なのでサーバーの秘密鍵が漏洩しても安心!

数学苦手だけど、以下みたいな感じです。

DHE.jpg

これでお互いに安全に秘密値(PreMaster Secret)を送り合えます。


DHEだと一時的に作成した鍵を使用するので復号が面倒くさい。
今回は簡単に復号できるRSAを使用します。

(上記server helloの暗号化方式でTLS_RSAに合意をしている)

共有秘密鍵の算出

これまでのやりとには、鍵を生成するための前準備でした。
これでクライアントとサーバー側で通信を暗号化するために使う鍵を生成するための情報が出揃いました。

ECDHEというアルゴリズムで算出してるみたいですが、割愛します!
ざっくりいうと以下の情報でいい感じに鍵を作るってこと見たいです。

・Hello Clientでクライアントが生成した乱数
・Server Helloでサーバーが生成した乱数
・PreMaster Secret

◯Change Cipher Spec

新しい暗号化仕様の利用開始を通知

これでTLSハンドシェイクは終了

2.データ通信

これで晴れて暗号化通信が出来るわけです。

暗号化されたデータ通信

リクエストデータ

request_cipher.jpg

レスポンスデータ

response_cipher.jpg

通信内容の復号化

上記のままだと暗号化されててわけわかめだから、以下の設定で事前に作成した証明書をインポートすることでTLS通信内容が復号化でき中身が確認できる。

Preference->Protocols->SSL->RSA Key List->Editを選択し、以下を設定。

IP Address-> 172.17.0.2
Port->443
Protocol->http
Key File->俺俺証明書key(server.key)

リクエストデータ

request_not_cipher.jpg

レスポンスデータ

response_not_cipher.jpg

雑感

ちょーざっくりメモでした

Please share  

MySQLのwait_timeout実験

秘伝のバッチが突如悲鳴を上げるということは少なくないと思いますが、自分の知識不足から復旧に時間を要してしまった。

自戒もかねてメモを残す。

クソクエリによるtimeoutでgone away

“MySQL server has gone away” が発生する原因としては以下の要因が考えられる。

  • クエリのサイズがでかすぎ
  • 接続のアイドルタイムアウト

前者はmax_packet_sizeの値を調整して解決できる。
が、今回の原因は後者のタイムアウトの秒数だった。

接続が開始されてから、最後にデータベースに問い合わせをした時間から、指定した秒数たってしまうと、データベースはクライアントとの接続を勝手に切ってしまう。
接続が切れた状態でクエリを送信すると、「おまえの接続はもう切れてるぜ!」となりgone awayとなる。

この指定した秒数の謎についておいかけていきたいと思います(^O^)/

なぜ原因判明まで時間がかかってしまったのか

そもそも時間がかかりすぎるクエリがよくないのだが、明らかに設定された時間内より早いタイミングで切断されていた。

デフォルトだとおそらく2880秒で設定されている

1
2
3
4
5
6
7
8
9
% mysql -u root
mysql> SHOW VARIABLES LIKE 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

2880秒もたってないのになぜgone awayになるんだよ。。。

マニュアルに書いてあった。。。

以下公式からの引用

wait_timeout.png

Interactive(対話型)で接続したやつはinteractive_timeoutの秒数をwait_timeoutとして設定しちゃうぜ!
ってことみたいです。

interactive_timeoutの秒数

1
2
3
4
5
6
7
8
9
% mysql -u root
mysql> SHOW VARIABLES LIKE 'interactive_timeout';
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.00 sec)

じゃあ対話型じゃない場合はどうなるかっていうと

1
2
3
4
5
6
7
% mysql -u root -e "show variables like 'wait_timeout'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 60 |
+---------------+-------+

my.iniで設定されている数字がやっとでてきた。

アプリケーションから接続する際は非対話モード(Interactiveじゃない)になるので、
60秒が設定されていたため、早いタイミングで落ちていたみたいです。

28800秒で設定されるから他に原因があるのかと思い余計な時間をとられてしまった。。。

実験

前提

  • mysql 5.6
  • php7.1
  • pdo

実験1. wait_timeoutを5秒に設定して、DBに接続してからsleepを6秒いれた後、クエリを実行するとgone awayになるか確認。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
define('WAIT_TIME', 6);
try {
$pdo = new PDO('mysql:host=db;dbname=test;charset=utf8','root','pass',
array(PDO::ATTR_EMULATE_PREPARES => false));
sleep(WAIT_TIME);
$sql = "SELECT version();";
$pdo->query($sql);
} catch (PDOException $e) {
exit($e->getMessage());
}

結果: 見事gone away!

1
Warning: PDO::query(): MySQL server has gone away in /src/connect.php on line 11

実験2. interactive_timeoutを5秒に設定して、対話接続でクエリ実行後に5秒まち、再度クエリを実行するとgone awayになるか確認。

1
2
3
4
5
6
7
8
9
% mysql -u root
mysql> SHOW VARIABLES LIKE 'interactive_timeout';
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.00 sec)

5秒待って再度クエリ実行

結果: 見事gone away!

1
2
mysql> SHOW VARIABLES LIKE 'interactive_timeout';
ERROR 2013 (HY000): Lost connection to MySQL server during query

テストに使ったコードはこちら

https://github.com/kuniiskywalker/test-mysql-goneaway

まとめ

対話型の場合だけ秒数が別指定が多めにとられてるのは、一旦接続していろいろいじることが前提なので当たり前といえば当たり前。。。

Please share  

場所が分かりにくいAmazonプライムビデオの視聴履歴リンクをメニューに表示させるchrome拡張機能

vis.png

夏も早いものでもう終わろうとしている今日この頃、皆さんはどのような動画をみてお過ごしでしょうか?
僕はAmazonプライムビデオのラインナップの豊富さをいいことに動画三昧の日々を送っています。
そんなAmazonプライムビデオのありそうでなかったchrome拡張機能を作ってみました。

場所が分かりにくい視聴履歴へのリンク

Amazonプライムビデオを使っていて以下のように思ったことはありませんでしょうか?

  • ちょっと前に見てた動画のタイトルを思い出せない!
  • スマホで途中まで見てたやつをPCでみたい!
  • 過去に視聴した動画を見たい、

そんなとき視聴履歴を見たいという思いはは至極当たり前の心理だと思いますが、なぜか視聴履歴までたどり着くには以下のステップを踏まなければなりません。

1) ヘッダーの「マイストアへ」をクリック

2) 「おすすめ商品を正確にする」をクリック

3) 画面左部の「視聴済みのビデオ」をクリック

そんな頻繁に見ることがないとはいえ、いざ見たいと思ったときにこのステップを思い出すのは至難の業です。。。

視聴履歴をみるための方法をgoogleで検索なんてしてたら時間が勿体無いですね。

たった一つの解決方法。。。

この視聴履歴の分かりずらさはサイトの回遊率を上げるためのものなのか、プライバシー的なものなのかはさておき、視聴履歴を見る方法だけで検索するとそれだけで記事になってしまいそうなニーズ感がある以上、この問題を解決するためのソリューションを提供することが僕の使命であることは自明である。

この問題をシンプルに解く方法、それは。。。。

そう、ヘッダーに「視聴履歴」のリンクを追加することです!

installed.jpg

使い方

chromeをお使いの方限定なのですが、以下URLから拡張機能の追加を行ってください。

https://chrome.google.com/webstore/detail/amazon-prime-video-fast-h/cfifaldajbjpfiafieifemojpfdiccbm

インストール後、再度Amazonプライムビデオのサイトに訪れるとヘッダーのメニューに「視聴履歴」へのリンクが追加されているはずです!

それではよいAmazonプライムビデオ生活を!!

機能に関するお問い合わせは以下からお願いします。
https://github.com/kuniiskywalker/AmazonPrimeVideoFastHistory/issues

Please share  

JSでバイナリを扱うことへの理解とメモ

ちょいちょい使うことがあるバイナリ操作処理だけど、イマイチ用語と用途が整理できていないのでメモ

背景

HTML5からバイナリが使えて便利最高!みたいな情報多いけど何が便利になってるのかよくわからない。
昔と比べてどう便利になったのかもふまえて整理してみる。

ゴール

JSでバイナリを使うことの利便性を享受できるようになる

まずバイナリが扱えるようになると何が嬉しいのか?

つーかバイナリって何?!

以下wikipedia引用


バイナリ(またはバイナリー:binary)とは、2進数のこと。 テキスト形式(文字データ)以外のデータ形式全般を指したり、コンピュータが処理・記憶するために2進化されたファイル(バイナリファイル)、またはその内部表現の形式(バイナリデータ、バイナリ形式)のことを指すのに用いられる。

つまり人間が読めないコンピューターだけが扱えるデータ形式

JSでバイナリが扱えなかった時代

  • バイナリを扱うためのAPIがなかったので、formタグでinput type=”file”を選択して送信するぐらいしかできなかった。
  • CSVやXML、画像ファイルなどのバイナリファイルはサーバー側に送って何かしらの処理を行っていた。

バイナリが扱えるようになった時代

  • HTML5以降にはいったFILE apiを使うことにより、バイナリの読み書きが行えるようになり、クライアント側だけで完結する処理が作れるようになった。
  • 画像をアップロードする前の画像をリサイズして送信したりとか、非同期で送信したりとか、アップロードするまえの画像をbase64エンコードしてimgタグに表示したりとかできて便利になった。

APIの種類と用途

Blob

  • バイナリデータを保持( immutable な生データ )
  • 値を取り出し不可( FileReaderやxhrなどを使用して中身をとりだせる)
  • サイズの確認や切り出しは可能(Blob.prototype.slice や Blob.prototype.size)
1
2
3
4
5
6
7
8
var req = new XMLHttpRequest();
req.open("GET", "https://i.imgur.com/rjI42fW.png", true);
req.onload = function(e) {
var blob = e.target.response; // これがBlob
};
req.send();

File

  • input type=”file”のファイル選択時にラップされるオブジェクト
  • Blobを継承している
  • Blobにファイル名や最終更新日の情報が追加されたもの( { name:String, lastModifiedDate:Date } )
    • ユーザーのシステム上のファイルをサポートするための機能を拡張
1
2
3
4
5
6
7
8
9
10
11
<input type="file" id="local_file">
input.onchange = function (event){
var input = event.target;
var file_list = input.files; // これはFileList
var file = file_list[0]; // これがFile
console.log(file.name);
console.log(file.lastModifiedDate);
};
</script>

ArrayBuffer

  • 物理メモリの領域(バッファ)を確保するためのクラス
  • バッファに対する操作はできない
    • バイト数取得と既存のバッファからあらたなバッファを作ったりしかできない
  • エンディアン方式がCPU依存
    • リトルエンディアンかビックエンディアンかを意識する必要がある
1
2
3
var buffer = new ArrayBuffer(12);
console.log(buffer.byteLength); // 12

Typed Array

  • 型付き配列
  • 型を指定してバッファから配列を生成
  • 「高速に」読み書きできる
  • 種類
    • Uint8Array
    • Uint16Array
    • Uint32Array
1
2
3
4
5
new Uint8Array([ (2 ** 8) -1 , 2, 3])
> Uint8Array(3) [255, 2, 3]
new Uint16Array([( 2 **16) - 1, 2, 3])
> Uint16Array (3) [65535, 2, 3]

DataView

  • TypedArrayよりも高機能なバッファ操作用クラス
  • ArrayBufferのエンディアン方式の差を吸収してくれる
    • リトルエンディアンかビックエンディアンかを意識する必要がない
1
2
3
4
var buffer = new ArrayBuffer(12);
var dataview = new DataView( buffer );
console.log(dataview);

BinaryString と DataURI と BlobURL

1
2
こういう形式のやつ
/9j/4AA~~~~~

DataURI

  • 画像のリソースをURIで表現できる
  • BinaryStringなのでサイズが1.3倍になる。。。
  • テキスト情報にバイナリデータをうめこめる(imgタグにうめこめる)
  • data: スキームが先頭についている URL
1
2
こういう形式のやつ
data:image/jpeg;base64,/9j/4AA~~~~~

FileReader使うと取得できる

1
2
3
4
5
6
7
8
9
10
11
12
<input type="file" id="local_file">
<script>
document.getElementById("local_file").onchange = function(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
var dataURL = reader.result; // これがDataURI
   };
  reader.readAsDataURL(input.files[0]);
};
</script>

↓imgタグに埋め込めば画像も表示できる

BlobURL

  • 画像のリソースをURIで表現できる
  • セッション内でユニークなURIを生成する
  • DataURIとちがってリソースへのポインター的な扱いになるからメモリにやさしい
1
2
こういう形式のやつ
blob:http://kuniiskywalker.github.io/f6ea8265-610f-4fbe-abfa-335f13cfe34c

createObjectURLでURIを取得

1
2
3
var blob = new Blob( ["文字列テスト"] , {type: "text/plain"} );
var blob_url = URL.createObjectURL(blob); // これがBlobURL
console.log(blob_url); // blob:http://kuniiskywalker.github.io/f6ea8265-610f-4fbe-abfa-335f13cfe34c

まとめ

クライアントサイドでのオフライン対応が充実していく背景には、じつはこういった便利APIを知らず知らずのうちに使っていたことを改めて認識できた。
サーバーで仕事をさせるかクライアントで仕事をさせるかは、通信時のデータ転送量をとるか端末のCPUコストをとるか、要件次第な感じ。

Please share  

jQueryプラグインを理解するためのjQuery関数とjQueryインスタンス

jQueryでプラグイン開発をする場合、jQuery.fnへの拡張を行ってするのはもはやテンプレなのですが、なぜfnへの拡張なのかをメモって見た。

jQueryインスタンス?

よく使う以下はjQueryインスタンスです

1
2
// セレクターを指定してjQueryにラップされたDOMオブジェクトが返る
$('#page');

もうちょっと細かく見ていきましょう。

この2つの実行形式は同じで、どちらもjQuery関数を実行している

1
2
3
$('#page');
jQuery('#page');

jQuery関数の中身はというと以下

1
2
3
4
var jQuery = function( selector, context ) {
// こいつが実行されている
return new jQuery.fn.init( selector, context, rootjQuery );
}

いつも、$(‘#page’)とかで取得してたやつの正体はこのnew jQuery.fn.initだった。

このjQueryインスタンスにはよく目にするメソッドが定義されている。

jquery_function.png

つまり、jqueryインスタンス自体に何かしらの拡張を行うには、このjQuery.fnへの拡張が必要になってくる。


jQueryオブジェクトへの拡張

jQuery自体もオブジェクトなのでメソッドを追加できる。
ここではインスタンスとの違いを見ていただきたい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// jQueryオブジェクトへの拡張したメソッドを定義
jQuery.output_message = function (msg) {
console.log(msg);
};
// jQueryオブジェクトを参照してメソッドを実行した結果
$.output_message("aaa");
> aaa
// jQueryインスタンスを参照してメソッドを実行した結果
$().output_message("aaa");
// インスタンス自体への拡張ではないのでエラーになる
> Uncaught TypeError: $(...).output_message is not a function
at <anonymous>:1:5

jQueryインスタンスへの拡張

こちらはpluginを作る時によく目にするやりかたで、jQuery.fnへの拡張を行う。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// jQueryインスタンスへの拡張を行ったメソッドを定義
jQuery.fn.output_message = function (msg) {
console.log(msg);
};
// jQueryオブジェクトを参照してメソッドを実行した結果
$.output_message("aaa");
// jQuery自体への拡張ではないのでエラーになる
> Uncaught TypeError: $.output_message is not a function
at <anonymous>:1:3
// jQueryインスタンスを参照してメソッドを実行した結果
$().output_message("aaa");
> aaa

jQueryプラグイン実装

上記を踏まえて、jQueryプラグインの実装例を見ていく。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 実行時にパラメーターを渡せる仕様になっていると便利
$.fn.hiddenItem = function (config) {
// デフォルトパラメーター
var defaults = {
speed: "slow"
};
var options = $.extend(defaults, config);
// thisはjQueryインスタンス
return this.click(function() {
// ここのthisはjQueryインスタンスではないので$(this)を実行してjQueryインスタンス化する必要がある
$(this).fadeOut(options.speed);
})
};
$("#header").hiddenItem({speed: "normal"})

プラグイン内メソッドの外部呼出し

DOM依存じゃないものや、プラグインのなかで使用しているメソッドを公開メソッドとして使いたいなどの場合は以下のような形で呼び出せる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var F = $.myPlugin = function () {
F.open.apply( this, arguments );
};
$.extend(F, {
// 公開用メソッド
testMethod: function () {
console.log("test");
}
});
$.fn.myPlugin = function (config) {
var defaults = {
message: "yes"
};
var options = $.extend(defaults, config);
return this.click(function() {
alert(options.message)
})
};
$.myPlugin.testMethod();
> test

まとめ

pluginの機能提供範囲を意識しながらつくるとよいのではという感じです。

Please share  

linuxのファイルのパーミッションの話

2016年一発目の記事はまさかのファイルパーミッションのお話!

なんとなくは分かっていたけど、具体的に説明できなかったのでこれまた備忘録になってしまいますがズラズラ書いてみます。

長いことweb開発やっていますが未だにそういえばパーミッション変更してねー!!みたいなことって結構あります(俺だけ)。

んでなんとなく、全員に書き込みも読み込みも実行もできるようにしちゃえばいんじゃね?みたいなことしゃいます(俺だけ)。

適切にパーミッションを設定しないと、ID・パスワードなどを設定してるファイルがみられちゃったり、ソースコードがみられてちゃったり、ファイルの一覧がみられちゃったり、プログラムが実行されちゃったり色々とリスクがあります。

では、用途に応じて適切なパーミッションを設定するにはどうしたらよいのか?

パーミッション関連の設定を一つずつ見ていきましょう。

パーミッション関連に必要設定項目概要

  1. 所有者と所属グループ
  2. 権限レベル
  3. 権限の適応範囲

1. 所有者と所属グループ

ファイル・ディレクトリには所有者と所属グループという権限が与えられている

【所有者】

・ファイルを所有してるユーザー
※基本的にはファイル・ディレクトリを作成したユーザーが所有者になる

【グループ】

・ファイルを所有しているグループ
※基本的にはファイル・ディレクトリを作成したユーザーが所属してるグループが所持しているグループになる

このようにファイル・ディレクトリに所有者・グループを割り当てることによって、
所有者ごとの権限・グループごとの権限など柔軟な権限レベルの設定を行うことが出来る。
グループのメリットとしては、特定のグループに所属してるユーザーにだけファイルの編集を行わせたい、閲覧をさせたいなどまとまった管理を行うことが出来る。


2. 権限レベル

r (読み込み) w (書き込み) x (実行)
ディレクトリの場合 ディレクトリの一覧の参照が可能 ディレクトリ内のファイルの削除、
名前変更が可能。新規作成が可能
ディレクトリにアクセス可能
ファイルの場合 ファイルをひらける ファイルを編集・更新できる プログラムを実行できる

よく見る755とか644とかの意味

r (読み込み) w (書き込み) x (実行)
2進数表記 100 010 001
8進数表記 4 2 1
  • 読み込みしかできない

    • r (読み込み) 4 = 4
  • 読み込みと書き込みができる

    • r (読み込み) 4 + w (書き込み) 2 = 6
  • なんでもできる

    • r (読み込み) 4 + w (書き込み) 2 + x (実行) 1 = 7

3. 権限の適応範囲

所有者

所有者の場合に適用される権限

グループ

所属してるグループの場合に適用される権限

他人

所有者でも所属グループでもない場合に適用される権限

表記例:(所有者権限)(グループ権限)(他人権限)

  • とりあえず所有者だけはなんでもできるようにしたい

    • 700
  • 所有者には書き込み/読み込みだけさせて、所属グループには読み込みだけさせたい

    • 644
  • 所有者にはなんでもさせて、所有者以外は書き込み/読み込みをさせたい

    • 755
  • 全員になんでもさせたい
    • 777

Please share  

git 備忘録

用語整理

  • ワーキングツリー:実際に作業中のファイルたち
  • ローカルレポジトリ: ローカルのレポジトリ
  • ステージング:コミット準備がととのった状態にあるファイルたち
  • リモートレポジトリ: リモートにあるレポジトリ(みんなで使うことが多いから共有レポジトリって呼んだりもする!)

1. 表示系

コミット履歴とかを表示する系のやーつ

コミット履歴表示

1
git log

変更したファイルを表示

1
git log --name-status

過去の特定のファイルの変更内容を見る

1
git log -p {path}

commitメッセージからキーワードで検索

1
git log --grep hoge

あのファイルのあの行誰が更新したのか確認

1
git blame

2.git commit

コミットする際につかうやーつ

とりあえず状況確認

どのファイルがステージングにあがっていてーとか、どのファイルが追加になったやつでーとかがみれる。

1
git status

修正前と修正後の差分を見る

コミットする前にどのファイルが更新されたか確認できる。

1
2
git diff
git diff {path}

ステージングにあげる

1
2
git add .
git add {ファイル名}

ステージングにあげたやつをとりけす(アンステージング)

1
2
git reset HEAD
git reset HEAD -- <ファイル名>

部分的にステージングにあげる

1
git add -p ファイル名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Blog {
private $title;
function __construct() {
}
+
+ public function set_title($title) {
+ $this->title = $title;
+ }
+
- public function get_title() {
- return $this->title;
- }
}
Stage this hunk [y,n,q,a,d,/,K,g,s,e,?]?

ハンク(ある程度に区切られた変更範囲)ごとに対話形式でどう対処するか選べる

y: ハンクをまるごとステージングへ
n: ハンクをステージングへあげない
e: 編集モード(vi)で部分的にステージングへあげる

  • ‘-‘の行をaddしたくなければ、’-‘を’ ‘(空白)にする。
  • ‘+’の行をaddしたくなければ、’+’の行を消す。
  • 先頭に#と書かれている行はaddされない

ステージングにあがっているファイルの差分を見る

1
git diff --cached

ステージングにあがっていないファイルの差分を見る

1
git diff

ファイル名変更

1
git mv -f js/app.js js/App.js

3.過去改竄系。。。

一個前のコミットをなかったことに

HEADだけ一個前に戻す

ステージング・ワーキングツリーは維持される。

1
git reset --soft HEAD^

HEAD・ステージングだけ一個前に戻す

ワーキングツリーは維持される。
一個前のコミットでアンステージングされた状態。

1
git reset --mixed HEAD^

HEAD・ステージング・ワーキングツリーを一個前に戻す

一個前のコミットでまっさらな状態になる。

1
git reset --hard HEAD^

一個前のcommitを編集

1
git commit --amend

例1) ファイルを追加しわすれた

1
2
git add aaa.txt
git commit --amend

例2) ファイルを間違って追加してしまった

1
2
git rm aaa.txt
git commit --amend

二個以上前のcommitを編集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 一旦編集対象のコミット履歴を下記コマンドで表示
# 最新の3コミットを対象にする
# git rebase -i HEAD~3
# 特定のコミット以前を対象とする(git log --onelineで一覧表示すると便利)
# git rebase -i {revision}~
# 出力結果
pick <コミット1のhash> test_1.htmlファイルを修正
pick <コミット2のhash> test_2.htmlファイルを修正
pick <コミット3のhash> test_3.htmlファイルを修正
# 編集漏れがあったコミットの「pick」の個所を「edit」に変更
pick <コミット1のhash> test_1.htmlファイルを修正
edit <コミット2のhash> test_2.htmlファイルを修正
pick <コミット3のhash> test_3.htmlファイルを修正
# その後
git add test_2.html
git commit --amend
git rebase --continue

一番最初のコミットをrebaseする←普通にやるとできないの以下の方法で!

1
git rebase -i --root

4.stash

変更中に他のブランチへの追加・変更をしたいときに一旦退避するやーつ

一時退避!

1
git stash save {スタッシュ名}

一時退避してるリスト

1
git stash list

リスト

1
2
3
<stash名1>: WIP on <stashを行ったブランチ名>: <ハッシュ> <コミットコメント>
<stash名2>: WIP on <stashを行ったブランチ名>: <ハッシュ> <コミットコメント>
<stash名3>: WIP on <stashを行ったブランチ名>: <ハッシュ> <コミットコメント>

復活

1
git stash save {スタッシュ名}

削除

1
git stash drop {スタッシュ名}

復活しつつ削除

1
git stash pop {スタッシュ名}

5. branch

ブランチのやーつ

ブランチ作成

1
git branch {ブランチ名}

ブランチ一覧表示

1
git branch

リスト

1
2
develop
*master

ブランチをきりかえる(チェックアウト)

1
git branch {ブランチ名}

ブランチを作成しつつチェックアウト

1
git checkout -b {ブランチ名}

ブランチ削除

1
git branch -d {ブランチ名}

ブランチを結合(マージ)

1
2
git checkout {マージさせたいブランチ名}
git merge --no-ff {マージしたいブランチ名}

ローカルブランチとリモートブランチの関連づけ

git pull origin masterってやらなくてもgit pullだけでよくなる

1
git branch --set-upstream develop origin/develop

6. remote操作

リモートのレポジトリのやーつ

リモートリポジトリ表示

1
git branch -a

リモートリポジトリをチェックアウト

コメント「-b」はローカルにリモートブランチのデータをおとしつつ、ローカルブランチの切り替えも行ってくれる。

1
2
3
git checkout -b {ローカルブランチ名} {リモートブランチ名}
# 例: git checkout -b develop origin/develop

リモートリポジトリをローカルから削除

1
2
3
4
git remote remove {名前}
# 例
$ git remote remove origin

リモートリポジトリを変更

1
git remote set-url origin {リモートブランチレポジトリ}

アーカイブ

変更された差分ファイルを抽出するのに便利そう

とりあえず全ファイルをzipでアーカイブ

1
git archive --format=zip HEAD -o archive.zip

あるコミットからの差分をzipでアーカイブ

1
2
# 3つ前のコミットからの差分
git diff --name-only HEAD HEAD~3 | xargs tar czf ./ver_diff.tar.gz
1
2
# あるコミットからあるコミットの差分
git diff --name-only コミットA コミットB | xargs tar czf ./ver_diff.tar.gz

運用編

リモートブランチとローカルブランチ※1を同期
※1. リモートブランチを参照してるローカルブランチ

1
git fetch

リモートブランチとローカルブランチの差分を表示

1
2
3
4
# 差分があるファイル一覧を表示
git diff --name-status リモートブランチ
# ファイルの差分を表示
git diff リモートブランチ

リモートブランチとローカルブランチをマージ

1
git merge リモートブランチ

repository_image.png

Please share  

mysqlのutf照合順序のメモ

utf8_bin

特徴

完全一致のみ

  • utf8…文字コードは UTF8 で判断しますよ!
  • bin…バイナリ判定(大文字と小文字は違う文字ですよ!全角と半角も違う文字ですよ!)

utf8_general_ci

特徴

拡張をサポートしない。

  • utf8…文字コードは UTF8 で判断する
  • general…大文字と小文字を同じ文字として扱い、全角と半角も区別する
  • ci…Case Insensitive(大文字と小文字を識別しませんよ!)

utf8_unicode_ci

特徴

拡張をサポートする。
半角カタカナ=>全角カタカナやひらがな=>カタカナのマッチングが可能。

  • utf8…文字コードは UTF8 で判断する
  • unicode…大文字と小文字を同じ文字として扱い、全角と半角は区別しない
  • ci…Case Insensitive(大文字と小文字を識別しませんよ!)

濁音、半濁音、ひらがな、カタカナも区別しない。
「は」で検索すると「は」「ば」「ぱ」「ハ」「バ」「パ」「ハ」がヒットする。

utf8_unicode_ciのデメリット

日本語の検索で、「ひらがな」「カタカナ」が混在した形でヒットしてしまう。
ひらがなの「ほ」で条件を指定したのに、「ぼ」や「ボ」や「ホ」といったものに一致してしまう。

余談

select時だけ照合順序を区別する方法
SELECTする時に条件として大文字小文字を区別したいのであれば、BINARYオペレータを記述する方法もあります。

1
SELECT * FROM fish WHERE BINARY name = ‘KATSUO’

Please share