君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

ファイルのタイムスタンプを保持したまま、scpコマンドで転送する

f:id:karoten512:20180506162850j:plain

いきさつ

サーバからファイルをダウンロードした後、

日付情報を用いてファイル処理をする必要があった。

コマンド

scpコマンドにpオプションを付けるだけ。

scp -rp remote_host:remote_dir local_dir

ちなみに

ファイル転送時に新しく作られるディレクトリについては、

どうしても転送時のタイムスタンプになるっぽい。

【OSSリーディング企画】BootstrapのJavaScriptを読み解く【その1】

f:id:karoten512:20180504205854p:plain

いきさつ

Bootstrap、すごい便利。

htmlタグさえドキュメントどおりにおけば*1

ルーセルは動くわモーダルは開くわアラートは出るわ、、、

f:id:karoten512:20180504211100j:plain

いつも本当にお世話になっております。

ただ、、、ただね、

なんで動くのか」について全然考えたことがなかったんですよ。

細かいところを調整しようと思った時に、

中がどうなっているのか分かっていないので、

手が完全に止まってしまう。。。

これじゃあ「ライブラリを使っている」のではなく、

「ライブラリに使われている」。そんな気がしてならない。。。

というわけで、OSSリーディングを始めました。

対象OSS

今回はBootstrapのモーダル周りについて読んでいきます。

読むファイルはこれ。

github.com

すでにビルドされている生のJavaScript

どうせならJavaScriptも軽く復習したい。

そういう思いもあります。

OSSを読むにあたって大切なこと

「全部は読まない。読みたいとこだけ」

なんだか懐かしい響きですね。

moto-neta.com

OSSを、はじめから全部読もうと思ってはいけません。

なんせOSS(今回はBootstrap)は、

「1000人以上の人が」

「何年もかけて」

開発をしているライブラリです。

はじめから全部読むのはツライツライ。

今回のゴール

何もしていないのに(JS書いていないのに)なんでボタンを押したらモーダルが開くのか

に絞って読んでいきます。

モーダルを開く処理までは読みません。

「ボタンを押したら」という部分に着目し、

ボタンに対してclickイベントを登録している部分を読み解きます。

また、その途中で即時関数が使われているので、即時関数についても簡単に触れます。

読んでいきます

Modalモジュールが宣言されている部分

/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.1.1): modal.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

var Modal = function ($$$1) {
   // いろいろな処理
}($);

だいたい1930行あたりにModalモジュール(モジュールという言い方が正しいのかどうかは微妙)が宣言されていますね。

Bootstrapでは様々な機能が使えますが、

これらはみなモジュールとして提供されています。

すごい乱暴に書くとこんな感じ。

var Modal = function ($$$1) {
   // いろいろな処理
}($);

var Carousel = function ($$$1) {
   // いろいろな処理
}($);

var Alert = function ($$$1) {
   // いろいろな処理
}($);

exports.Alert = Alert;
exports.Carousel = Carousel;
exports.Modal = Modal;
...

各モジュールで使われる変数は関数の中で定義され、カプセル化されています。

それにより、他のモジュールで使われている変数とぶつかったりすることはありません。

かしこい(小並感)。

exportsの意味について

そして、

exports.Alert = Alert;
exports.Carousel = Carousel;
exports.Modal = Modal;

exportsにはwindow.bootstrapオブジェクトが入っているよ。*2

つまり、もっと分かりやすく書くとこんな感じ。

window.bootstrap.Alert = Alert;
window.bootstrap.Carousel = Carousel;
window.bootstrap.Modal = Modal;

なので、コンソール上でwindow.bootstrapすると、

これらのモジュール(正確にはコンストラクタ)にアクセスすることができるよ。

f:id:karoten512:20180504213318p:plain

Modal用のボタンにクリックイベントをバインドしている部分

さて、Modalを開くためのボタンのhtmlを確認してみるよ。

<button type="button" data-toggle="modal" data-target="#exampleModal">
  Launch demo modal
</button>

これだけでいいらしいよ。ふざけてるね!

重要なのは、

data-toggle="modal"

という部分。

これを目印にして、

このdata属性を持っている要素にclickイベントをバインドしているよ。

こんな感じ!

// 2500行あたり
$$$1(document).on('click', '[data-toggle="modal"]', function (event) {
  // クリックした時に起こるイベント
}

え?

でもなんで勝手にclickイベントがバインドされるかって?

それは、この処理が即時関数の中で実行されているからだよ!

var Modal = function ($$$1) {
   // 即時関数の中
  $$$1(document).on('click', '[data-toggle="modal"]', function (event) {
    // クリックした時に起こるイベント
  }
}($);

一度JavaScriptが読み込まれると即時関数は勝手に実行されるので、

html上にdata-toggle="modal"さえ書いておけば、

勝手にclickイベントがバインドされるんだね!

即時関数の使い所

「一度しか実行されない処理」を即時関数で書くことが多いよ。

今回のように、

「特定の要素に対してイベントリスナを登録」

という処理は1度だけの実行でいいから、よく使われるよ。

即時関数のメリット

即時関数で処理を包むことにより、

変数のスコープが即時関数内に限定されるので、

グローバルスコープを汚さないよ!

巷のライブラリでも即時関数はよく見るよ。みてみてね!

まとめ

というわけで今回は、

モーダルを開くためのボタンに対してclickイベントが登録されている部分を確認してみました。

data-toggle='modal'さえ持っていればよいので、

ボタンだろうがlistだろうがdivだろうがなんでもモーダルが開けそうですね。

BootstrapのJavaScriptは、

定数がきちんと宣言されていたり、

jQueryからモーダルを操作できるようなAPIを提供していたり、

複数のモーダルを開くことができるような実装の仕方をしていたりと、

すごく勉強になる部分が多いので、ひきつづき読んできたいと思います。

*1:JavaScript書かなくてよいことも多い

*2:気になる人は1行目から読んでみよう。Boostrapがブラウザ上で実行される場合、exportsにはwindow.bootstrapオブジェクトが代入されているはずだ

つるのおんがえし〜「決して、この部屋をのぞかないで(Permission denied)」〜

f:id:karoten512:20180403135544j:plain

この記事のゴール

この記事を読めば、

$ ls hoge
d rwx-----x. 1 owner  group 4096 Dec 22 15:48 hoge

この

rwx-----x

が読めるようになる(ハズ)です。

あらすじ

おじいさんが罠にかかっているつるを見つけました。

苦しんでいるつるがかわいそうだったので、おじいさんは助けてやりました。

その日の夜は大雪になり、とても寒い夜でした。

コン、コン。家の扉が叩かれる音がします。

夜、一人のおんながおじいさんの家にやってきました。

そのおんなは一晩泊めてほしいといいます。

やさしいおじいさんはおんなを家に招き入れることにしました。

家に招き入れた

おじいさんは手元の端末でおんなをlinux userに追加しました。

[root@ie ~]# useradd tsuru

おんながはたを折り始めた

おんなはいろいろ良くしてもらったお礼に、

はた(美しい布)をおりたいといいました。

おんな「はたをおりあげるまで、決してのぞかないでください」

[tsuru@ie ~]$ cd tsuru

といって、おんなはへやにはいっていきました。

おじいさんは好奇心に耐えられなかった

おじいさんは震える手で端末にこう打ち込みました。

[jiisan@ie ~]$ cd tsuru

しかし、

-zsh: cd: tsuru/: Permission denied 

部屋には入れませんでした。

[jiisan@ie ~]$ ls tsuru
drwx------. 2 tsuru   tsuru  4096  3月 27 07:26 2018 tsuru

おじいさん「なるほど。。。こりゃ入れんわい」

おじいさんは納得しました。

なぜ、入れなかったのか

まず、おじいさんがuseraddしたことにより、

tsuruディレクトリが作成されました。

f:id:karoten512:20180325083108p:plain

useraddしたときにできるこの部屋の所有者は「tsuru」になります。

ですので、こうなります。

f:id:karoten512:20180325083237p:plain

この部屋は、作成された時から以下の要件をみたしています。

・つる[owner]は部屋を自由にする(見れる、いじれる、入れる)ことができる

・おじいさんとおばあさん[other]は入れない

つまり、こういうことですね。

f:id:karoten512:20180325083708p:plain

もうすこしlinux風に書くとこんな感じです。

f:id:karoten512:20180325083839p:plain

さらに、linuxのファイルは「所有者」だけでなく「所有グループ」をという情報を持っています。

f:id:karoten512:20180326000511p:plain

これにより、

「つるの親族」というグループのユーザだけ部屋に入ることを許可できたりします。

useraddしたときにできる部屋の所有グループは、「tsuru」です。

f:id:karoten512:20180326000524p:plain

これで完成です。

ちょっと見方を変えて、、、

f:id:karoten512:20180326001214p:plain

ここでlsです!

f:id:karoten512:20180326001345p:plain

「owner」ができること

「group」ができること

「other」ができること

をまとめたものを、パーミッションと言います。

おじいさんは強硬手段に出た

中でなにをやっているのか、気になって仕方がありません。

じいさんの秘蔵のこれくしょんが、

つるによって紐解かれていしまっているかも知れません。

3日悩んでおじいさんは決心しました。

パーミッションを変えよう」

(つづく)

【Java言語で学ぶリファクタリング入門】クラスの抽出

いきさつ

昔書いたコードが悲しい感じだった&どこから手を付ければよいかわからない感じだったので、

リファクタリングの知識が欲しかった。

そしたら手元にこの本があった。

リファクタリング

Playerクラス

class Player {
    private _currentMedia;
    private _musicData;
    private _videoData;

    public setMedia() { };
    public play() { };
    public loop() { };
    public stop() { };

    private playMusic() { };
    private loopMusic() { };
    private stopMusic() { };
    private playVideo() { };
    private loopVideo() { };
    private stopVideo() { };
}

playerが責務を持ちすぎ。

  • musicの再生・停止・ループ方法を知っている
  • videoの再生・停止・ループ方法を知っている

なにがしんどいのか

新しいmediaを追加するたびにmethodを追加する必要がある。

playerが再生されるものに対しての知識を持ちすぎているので、密結合。

リファクタリング方法

クラスの抽出を行なう。

リファクタリング

新たにMusicMediaとVideoMediaクラスを作った。

Mediaクラス

/** 共通インターフェース */
interface Playable {
    play();
    loop();
    stop();
}

/**MusicMedia */
class MusicMedia implements Playable {
    public play() { };
    public stop() { };
    public loop() { };
}

/** VideoMedia */
class VideoMedia implements Playable {
    public play() { };
    public stop() { };
    public loop() { };
}

Playerクラス

class Player {
    private _currentMedia: Playable;

    public setMedia(media: Playable) {
        this._currentMedia = media;
    }
    public play() {
        this._currentMedia.play();
    }
    public stop() {
        this._currentMedia.stop();
    }
    public loop() {
        this._currentMedia.loop();
    }
}

何がうれしいのか

  • 新たにMediaを追加する時に、既存のコードに手を加えなくて良い(拡張に対して開いている)

  • 既存のmediaの修正をする時に、クラス内で修正が収まる(修正に対して閉じている)

  • PlayerクラスはPlayableインターフェースを実装しているクラスなら何でも扱えるので、疎結合

【Node.js】Expressをinstallして起動(npm start)するまで

f:id:karoten512:20180322123340j:plain

いきさつ

少し作りたいものがあったので、

Expressをinstallして起動してみる

手順

express-generatorをグローバルインストール

$ npm install express-generator -g  

expressコマンドでプロジェクトを作成

$ express restapi

必要なnode_moduleをinstall

$ cd restapi; npm install

アプリケーションの起動

$ npm start

> restapi@0.0.0 start /home/vagrant/dev/restapi
> node ./bin/www

ポートにアクセス

ipアドレス:3000

にアクセスすると、

f:id:karoten512:20180322123120p:plain

と表示され、起動が確認できる。

おまけ

ポート3000がLISTENになっているか見てみる。

$ netstat -antp
tcp        0      0 :::3000    :::*   LISTEN      22175/node

ポート3000をnodeプロセスがLISTENしている。

【CTF】サーバをハックする〜Simple Auth IIとSQLiteと〜

f:id:karoten512:20180322104803j:plain

いきさつ

セキュリティの勉強も兼ねてCTFの問題を解いてみた。

サーバをハックするより前にソースコードが見えているので

ハックするような大したことはしていない。

問題

ksnctf.sweetduet.info

20ptなので超簡単ということだが。。。?

考えたこと

ログイン画面

f:id:karoten512:20180322104951p:plain

適当なID&パスワードを入れ、ログインしてみる。

f:id:karoten512:20180322105024p:plain

ソースコード

<?php

function h($s)
{
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

if (!isset($_POST['id']) or !is_string($_POST['id']))
    $_POST['id'] = '';
if (!isset($_POST['password']) or !is_string($_POST['password']))
    $_POST['password'] = '';

$try = false;
$ok = false;

if ($_POST['id']!=='' or $_POST['password']!=='')
{
    $try = true;
    $db = new PDO('sqlite:database.db');
    $s = $db->prepare('SELECT * FROM user WHERE id=? AND password=?');
    $s->execute(array($_POST['id'], $_POST['password']));
    $ok = $s->fetch() !== false;
}

postされたidとpasswordでuserテーブルを検索、

該当レコードが存在する場合はログイン成功ということらしい。

SQLインジェクション使える?→使えないよね

プリペアドステートメントを使用しているので、

execute内に渡された値はエスケープされる。

この時点で

やれることがなくなってしまった。。。

ググる

timeislong.hatenablog.com

この記事をみると、

「ダウンロード」という言葉がある。

どういうこと?

SQLiteについて調べる

どうやらSQLiteのDB情報は、

****.dbというファイル形式で保存されるらしい。

そして、

<?php
//...

$db = new PDO('sqlite:database.db');

このsqliteの後で指定されているのはDBのファイルで、

相対パス指定されているということらしい。

DBファイルをダウンロード

sourceのurlのauth.phpの部分を、database.dbに変えるとDBファイルがダウンロードできる。

(普通じゃ考えられないけど)

SQLiteで読み込む

$ sqlite3 ~/Downloads/database.db 
sqlite>

適当なsqlを発行

phpソースコードから、userテーブルが有ることがわかっているので、

selectで全部持ってくる。

sqlite> select * from user;
root|FLAG_****

でてきた。

まとめ

この手の問題を特には様々な知識が求められる。

今回で言えば、

などなど。

なかなか勉強になった。

#Cでsocket通信をしてみる。ついでにnetstatでlistenしているポートを調べる。

C#でソケット通信

職場でC#を使うのと、

ソケット通信に興味があるのでちょっと練習してみた。

ソースコード

server側

using System;

public class Server
{
  public static void Main()
  {
    string ipString = "127.0.0.1";

    System.Net.IPAddress ipAdd = System.Net.IPAddress.Parse(ipString);

    int port = 2001;

    System.Net.Sockets.TcpListener listener =
      new System.Net.Sockets.TcpListener(ipAdd, port);

    listener.Start();

    Console.WriteLine("Listenを開始しました({0}:{1})。",
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Address,
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Port);


    // 接続要求があったら受け入れる
    System.Net.Sockets.TcpClient client = listener.AcceptTcpClient();
    Console.WriteLine("クライアント({0}:{1})と接続しました。",
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Port);

    // NetworkStreamを取得データの流れ
    System.Net.Sockets.NetworkStream ns = client.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;

    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    bool disconnected = false;
    System.IO.MemoryStream ms = new System.IO.MemoryStream();

    byte[] resBytes = new byte[256];
    int resSize = 0;


    do
    {
      //データの一部を受信する
      resSize = ns.Read(resBytes, 0, resBytes.Length);
      //Readが0を返した時はクライアントが切断したと判断
      if (resSize == 0)
      {
        disconnected = true;
        Console.WriteLine("クライアントが切断しました。");
        break;
      }
      //受信したデータを蓄積する
      ms.Write(resBytes, 0, resSize);
      //まだ読み取れるデータがあるか、データの最後が\nでない時は、
      // 受信を続ける

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    //受信したデータを文字列に変換
    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    if (!disconnected)
    {
      //クライアントにデータを送信する
      //クライアントに送信する文字列を作成
      string sendMsg = resMsg.Length.ToString();

      //文字列をByte型配列に変換
      byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

      //データを送信する
      ns.Write(sendBytes, 0, sendBytes.Length);
      Console.WriteLine(sendMsg);
    }

    ns.Close();
    client.Close();
    Console.WriteLine("クライアントとの接続を閉じました。");

    listener.Stop();
    Console.WriteLine("Listenerを閉じました。");

    Console.ReadLine();
  }

}

clinent側

using System;

public class Client
{
  public static void Main()
  {
    Console.WriteLine("入力してください");
    string sendMsg = Console.ReadLine();

    if (sendMsg == null || sendMsg.Length == 0)
    {
      return;
    }

    string ipOrHost = "127.0.0.1";
    int port = 2001;


    System.Net.Sockets.TcpClient tcp =
      new System.Net.Sockets.TcpClient(ipOrHost, port);
    Console.WriteLine("サーバー({0}:{1})と接続しました({2}:{3})。",
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Port,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Port);


    System.Net.Sockets.NetworkStream ns = tcp.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;


    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

    ns.Write(sendBytes, 0, sendBytes.Length);
    Console.WriteLine(sendMsg);


    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    byte[] resBytes = new byte[256];
    int resSize = 0;
    do
    {
      resSize = ns.Read(resBytes, 0, resBytes.Length);

      if (resSize == 0)
      {
        Console.WriteLine("サーバーが切断しました。");
        break;
      }

      ms.Write(resBytes, 0, resSize);

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    ns.Close();
    tcp.Close();
    Console.WriteLine("切断しました。");

    Console.ReadLine();
  }
}

コンパイル

mcs client.cs
mcs server.cs

実行

サーバの実行

$ mono server.exe
Listenを開始しました(127.0.0.1:2001)。

クライアントの実行

$ mono client.exe 
入力してください
hello world

サーバの反応

クライアント(127.0.0.1:32924)と接続しました。
hello world
11
クライアントとの接続を閉じました。
Listenerを閉じました。

まとめ

TCP通信というのは、IPアドレスとポートの組み合わせ。

だからlocalhost上でポートさえ違えば、通信ができる。

今回は

・サーバ  IP 127.0.0.1  ポート 2001

・クライアント  IP 127.0.0.1  ポート 32924

で通信を行った。

なお、クライアントのポートは通信毎に自動で割り振られる。

netstatでポートがLISTENしているかどうかを調べてみる

$ netstat -anpt
tcp        0      0 127.0.0.1:2001              0.0.0.0:*                   LISTEN      14145/mono  

monoというプロセスが、 2001ポートを使って通信していることがわかる。

-aオプション

LISTEN状態も含むすべてのソケットを表示する。

-nオプション

IPアドレス・ポート番号をそのまま表示する。

これがない場合、ドメイン名として表示されたり、sshhttpdとか表示されたりする。

-pオプション

通信しているプロセスを表示する。