<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Inhale n&#039; Exhale</title>
	<atom:link href="https://h2plus.biz/hiromitsu/feed" rel="self" type="application/rss+xml" />
	<link>https://h2plus.biz/hiromitsu</link>
	<description>what we need to live a life</description>
	<lastBuildDate>Tue, 07 Aug 2018 06:24:20 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.6.17</generator>
	<item>
		<title>Let&#8217;s Encryptで無料SSL証明書をインストールしてみた</title>
		<link>https://h2plus.biz/hiromitsu/entry/890</link>
					<comments>https://h2plus.biz/hiromitsu/entry/890#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Fri, 05 Feb 2016 08:53:28 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[Let's Encrypt]]></category>
		<category><![CDATA[インストール]]></category>
		<category><![CDATA[無料SSL証明書]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=890</guid>

					<description><![CDATA[<p>去年の12月にLet&#8217;s Encryptがパブリックベータ版に移行したというのがニュースになったとき、管理しているサイトの1つ（stingrays.tokyo）を使って試してみようかなぁと思ったものの、いろい <a href='https://h2plus.biz/hiromitsu/entry/890' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/890">Let’s Encryptで無料SSL証明書をインストールしてみた</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<!-- Easy Plugin for AdSense V8.63 -->
<!-- [leadin: 1 urCount: 1 urMax: 0] -->
<div class="ezAdsense adsense adsense-leadin" style="text-align:center;margin:12px;"><script type="text/javascript"><!--
google_ad_client = "ca-pub-1919376341395637";
/* 468x60 Inhale &#39;n Exhale エントリー */
google_ad_slot = "6736233253";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>
<!-- Easy Plugin for AdSense V8.63 -->
<p>去年の12月に<a href="https://letsencrypt.org/2015/12/03/entering-public-beta.html" target="_blank">Let&#8217;s Encryptがパブリックベータ版に移行した</a>というのがニュースになったとき、管理しているサイトの1つ（<a href="https://stingrays.tokyo/" target="_blank" class="broken_link">stingrays.tokyo</a>）を使って試してみようかなぁと思ったものの、いろいろ忙しかったのもあり早2ヶ月が経ってしまったが、FacebookのTLでちょうど環境が一致する<a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-let-s-encrypt-certificates-for-multiple-apache-virtual-hosts-on-ubuntu-14-04?utm_content=how-to-set-up-let-s-encrypt-certificates-for-multiple-apache-virtual-hosts-on-ubuntu-14-04" target="_blank">チュートリアルのブログポスト</a>を見つけたので、<a href="https://letsencrypt.readthedocs.io/en/latest/" target="_blank">本家のドキュメント</a>を読みつつ手を動かしてみた。</p>
<p>結論を先に言うと、ちょっとつまづいたけど簡単にできた。</p>
<p><span id="more-890"></span><br />
まず、サーバー環境に関して前提条件がいくつかある。</p>
<ol>
<li>サーバーに非root権限でターミナルに接続（SSHなど）できること</li>
<li>sudoなどでroot権限での実行が可能であること</li>
<li>インターネット上に公開されている（DNSで名前解決できる）稼動中のWebサーバーであること</li>
</ol>
<p>上記の質問で1つでも答えがノーならば、ここでおしまい。<br />
基本的に共用レンタルサーバーとかでは使えない。</p>
<p>ちなみに今回使った環境は、さくらVPS上のUbuntu Server 14.04.3 LTS + Apache 2.4.7で、証明書がなかったのでHTTPだけで運用していたサーバー。</p>
<p>では早速Let&#8217;s Encryptをインストールしてみよう。と言っても、<code>apt-get install</code>でのインストールではなく、単にGitHubからソースを<code>git clone</code>するだけ。</p>
<pre>
$ sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
</pre>
<p>インストール先は<code>/opt</code>にしたが、好みの問題でもあるので、別に<code>/usr/local</code>の下とかでも構わない。<br />
Let&#8217;s EncryptはGitHub上で開発途上なんで、直近の<a href="https://github.com/certbot/certbot/releases" target="_blank">リリースタグ</a>で固定しておいた方が安心かも。</p>
<pre>
$ cd /opt/letsencrypt
$ sudo git co -b v0.3.0 v0.3.0
</pre>
<p>続いてLet&#8217;s Encryptクライアントを介してサーバー証明書のインストールをする。</p>
<pre>
$ ./letsencrypt-auto --apache -d stingrays.tokyo
</pre>
<p>が、エラー発生&#8230;。</p>
<pre>
Domains: stingrays.tokyo
Error: The server could not connect to the client for DV
</pre>
<p>ネットで同じエラーを探してみたら、「Let&#8217;s EncryptのCAが証明書のインストール先になるWebサーバーの名前解決できてないんじゃね？」という回答が多かったが該当せず。</p>
<p>HTTPのみで運用しているというのが原因で、自己署名証明書でもいいからとりあえずHTTPSでアクセスできるようにしておかないといけなかった。</p>
<pre>
$ sudo a2enmod ssl
$ sudo a2ensite default-ssl
$ sudo service apache2 restart
$ curl -kI https://localhost
HTTP/1.1 200 OK
(略)
</pre>
<p>HTTPSでのアクセスはOK！<br />
気を取り直して<code>letsencrypt-auto</code>を再実行するも失敗&#8230;ここがつまづいた部分。</p>
<p><code>curl</code>だけでHTTPSにつながることを確認したけど、手元のブラウザで確認するのを怠っていた。ブラウザからHTTPSでつながらない&#8230;ってことは、今までHTTPオンリーで運用していたから<code>iptables</code>で穴が開いてなかったというオチ。</p>
<p>改めて<code>letsencrypt-auto</code>を再実行！通った！</p>
<p><code>letsencrypt-auto</code>は非root権限で実行しているのに、rootじゃないと編集できない<code>/etc/apache2</code>配下の設定ファイルが書き換わるのはなぜだろう？直近で<code>sudo</code>してたからプロンプトしなかっただけ？<br />
Let&#8217;s Encryptがどう設定を書き換えるのか気になる人は、<code>/etc/apache2</code>配下を<code>git</code>で管理するといいでしょう。</p>
<pre>
$ cd /etc/apache2
$ sudo git init
$ sudo git add .
$ sudo git ci -m 'Initial config'
</pre>
<p><code>letsencrypt-auto</code>の実行後に<code>git diff</code>すれば変更点が一目瞭然。</p>
<p>ちなみに、Apache2の場合、<code>/etc/letsencrypt/options-ssl-apache.conf</code>というファイルができあがり、<code>mod_ssl</code>用の推奨設定みたいなのが含まれているので、実際に運用している設定で欠けている部分があったらコピペして補完しとくといいでしょう。</p>
<p>これでLet&#8217;s Encryptの無料SSL証明書を使ってHTTPSがサービスできるようになったわけだ。</p>
<p><img src="/hiromitsu/wp-content/uploads/2016/02/certificate.png" alt="Let's EncryptのCAから発行されていることがわかる" /></p>
<p><a href="https://www.ssllabs.com/ssltest/" target="_blank">SSL Report</a>でも正常なHTTPS通信ができているという確認ができる（自分のサイトで試してください）。</p>
<p>めでたしめでたし&#8230;ではなく、Let&#8217;s Encryptの証明書の有効期間は90日しかないので、証明書が期限切れになる前に都度更新（Renew）しなければならない。現時点ではLet&#8217;s Encryptから証明書を自動更新する仕組みは提供されていないようだが、<a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-let-s-encrypt-certificates-for-multiple-apache-virtual-hosts-on-ubuntu-14-04?utm_content=how-to-set-up-let-s-encrypt-certificates-for-multiple-apache-virtual-hosts-on-ubuntu-14-04" target="_blank">冒頭で紹介したブログポスト</a>で便利なシェルスクリプトを公開してくれていた。</p>
<pre>
$ sudo curl -L -o /usr/local/sbin/renew-letsencrypt http://do.co/le-renew
$ sudo chmod +x /usr/local/sbin/renew-letsencrypt
</pre>
<p>短いスクリプトなので、中で何をやっているのかザッと目を通しておくといいだろう。<br />
最後に、このスクリプトを<code>cron</code>で定期実行するように設定すればおしまい。</p>
<pre>
$ sudo crontab -e
# 以下の1行を追記
0 3 1 */2 * /usr/local/sbin/renew-letsencrypt stingrays.tokyo >> /var/log/renew-letsencrypt.log
</pre>
<p>有効期間は90日だがバッファを設けて、偶数月の1日、つまり約60日間隔で更新処理が実行されるように仕込んでおいた。これで完全放置プレイで無料SSLが実現できたわけである。</p><p>The post <a href="https://h2plus.biz/hiromitsu/entry/890">Let’s Encryptで無料SSL証明書をインストールしてみた</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/890/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>IEのHTMLAnchorElement.pathnameの実装</title>
		<link>https://h2plus.biz/hiromitsu/entry/887</link>
					<comments>https://h2plus.biz/hiromitsu/entry/887#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Wed, 16 Sep 2015 21:54:01 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[HTMLAnchorElement]]></category>
		<category><![CDATA[IE]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=887</guid>

					<description><![CDATA[<p>状況に応じてフォームの送信先URL（formタグのaction属性値）のクエリーストリングを書き換えるという処理をするために、こんなJavaScriptを書いてみた。 しかし、 フォームを表示しているページのURLがWe <a href='https://h2plus.biz/hiromitsu/entry/887' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/887">IEのHTMLAnchorElement.pathnameの実装</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>状況に応じてフォームの送信先URL（formタグのaction属性値）のクエリーストリングを書き換えるという処理をするために、こんなJavaScriptを書いてみた。</p>
<pre class="brush: jscript; title: ; notranslate">
function updateQuery(query)
{
  var form = document.forms[0]
    , action = document.createElement('a')
  ;

  // オリジナルの送信先URLからパス部分を切り出して、クエリーストリングだけを書き換える。
  action.href = form.getAttribute('action');
  form.setAttribute('action', action.pathname+'?'+query);
}
</pre>
<p>しかし、</p>
<ul>
<li>フォームを表示しているページのURLがWebサイトのルート直下ではない
<li>オリジナルのフォームの送信先URLが絶対パスで書かれている</li>
</ul>
<p>という条件下では、フォームを送信した時に<strong>IEでだけ</strong>「404 Not Found」になってしまった。</p>
<p><span id="more-887"></span><br />
例えば <code>http://yourdomain.com/welcome/form.html</code>というページにフォームがあって、formタグのaction属性値が<code>/app/update</code>だったとすると、<code>updateQuery('xxx')</code>が実行された後にフォームを送信すると、<code>http://yourdomain.com/welcome/app/update</code>にリクエストが飛んでしまう。</p>
<p>どうやらHTMLAnchorElementの実装がIEだけ違うみたいで、pathnameプロパティの先頭にスラッシュが付いていない。</p>
<p>試しに、<a href="https://jsfiddle.net/f8octx78/">JSFiddleにテストコードを書いてみた</a>ところ、IEだけ「<code>path/to/resource</code>」となってしまう。他のブラウザはみんな「<code>/path/to/resource</code>」になる。</p>
<p>IEは常に混乱をもたらしてくれる&#8230;。</p>
<p>ということで、クロスブラウザ対応するには</p>
<pre class="brush: jscript; title: ; notranslate">
function updateQuery(query)
{
  ...（略）...
  form.setAttribute('action', (action.pathname.charAt(0) == '/' ? '' : '/')+action.pathname+'?'+query);
}
</pre>
<p>と書いてやらなければならない。</p>
<p><code>location.pathname</code>はIEでもちゃんとスラッシュが先頭に付いているのにね&#8230;やれやれ。</p>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/887">IEのHTMLAnchorElement.pathnameの実装</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/887/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Zend_Db_Selectで複数テーブルからデータを取得したいのだが&#8230;</title>
		<link>https://h2plus.biz/hiromitsu/entry/870</link>
					<comments>https://h2plus.biz/hiromitsu/entry/870#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Sat, 24 Jan 2015 10:29:00 +0000</pubDate>
				<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[COUNT]]></category>
		<category><![CDATA[GROUP BY]]></category>
		<category><![CDATA[INNER JOIN]]></category>
		<category><![CDATA[Zend_Db_Expr]]></category>
		<category><![CDATA[Zend_Db_Select]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=870</guid>

					<description><![CDATA[<p>Zend FrameworkでWebアプリを作ることになり、久しぶりにZend Frameworkのリファレンスを見ながらコードを書いていたのだが、ものすごく単純なことなはずなのにZend_Db_Selectの仕様（？） <a href='https://h2plus.biz/hiromitsu/entry/870' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/870">Zend_Db_Selectで複数テーブルからデータを取得したいのだが…</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Zend FrameworkでWebアプリを作ることになり、久しぶりにZend Frameworkのリファレンスを見ながらコードを書いていたのだが、ものすごく単純なことなはずなのに<code>Zend_Db_Select</code>の仕様（？）のせいでちょいとハマっていた。</p>
<p>Webアプリではお決まりのユーザー管理機能なんだが、話を単純にするためにやりたいことだけに焦点を当てるために、以下のような<code>roles</code>テーブルと<code>users</code>テーブルがあるとしよう。</p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE roles (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL UNIQUE
);

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL UNIQUE
  role INT FOREIGN KEY REFERENCE
);
</pre>
<p>ユーザーにロールを割り当てる典型パターン。<br />
で、やりたいことは、</p>
<p><strong>ロールの一覧表示をする際に、そのロールが割り当てられているユーザー数も出したい！</strong></p>
<p><span id="more-870"></span><br />
もちろん、SQL一発で簡単に取得できる。</p>
<pre class="brush: sql; title: ; notranslate">
SELECT roles.*,count(users.role) AS users
FROM roles,users
WHERE roles.id = users.role
GROUP BY roles.id;
</pre>
<p>このSQLを<code>Zend_Db_Select</code>で実装しようと思って、<code>FROM</code>句に2つのテーブルを並べるために</p>
<pre class="brush: php; title: ; notranslate">
$select = $db-&gt;select();
$count = new Zend_Db_Expr('count(users.role)');
$select-&gt;from('roles')
       -&gt;from('users', array('roles.*', 'users' =&gt; $count))
       -&gt;where('roles.id = users.role')
       -&gt;group('roles.id');
var_dump($select-&gt;__toString()); 
</pre>
<p>とやってみたが&#8230;</p>
<pre class="brush: sql; title: ; notranslate">
SELECT roles.*,count(users.role) AS users
FROM roles INNER JOIN users
WHERE roles.id = users.role
GROUP BY roles.id;
</pre>
<p>と、2つのテーブルが<code>INNER JOIN</code>で結合するようなSQL文ができてしまった。</p>
<p>SQLを実行しても</p>
<pre>
ERROR:  syntax error at or near "WHERE"
LINE 3: WHERE roles.id = users.role
        ^
</pre>
<p>とエラーになってしまう（ちなみにRDBにはPostgreSQLを使っている）。</p>
<p><code>INNER JOIN</code>じゃなくてカンマでテーブル名を並べる方法はないものかと<code>Zend_Db_Select</code>のコードも調べてみたが</p>
<pre class="brush: php; title: ; notranslate">
public function from($name, $cols = '*', $schema = null)
{    
    return $this-&gt;_join(self::FROM, $name, null, $cols, $schema);
}
</pre>
<p>となっているので、<code>JOIN</code>する以外、選択の余地がないようで&#8230;。</p>
<p>最終的には、それぞれのテーブルに対応する<code>Zend_Db_Table</code>クラスを作って、それぞれのテーブルから<code>fetchAll()</code>して、PHP上で結合させるという方法に落ち着いた。SQLが2発実行されるのが嫌だけど、パフォーマンスがシビアになるWebアプリでもないので、コードの可読性を重視して。</p>
<pre class="brush: php; title: ; notranslate">
class RolesController extends Zend_Controller_Action
{
  public function indexAction()
  {
    $table = new DbTable_Roles;
    $roles = array();
    $users = $this-&gt;_getRefUsers();
    foreach ($table-&gt;fetchAll($table-&gt;select()) as $row) {
      $role = (object)$row-&gt;toArray();
      $role-&gt;users = array_key_exists($role-&gt;id, $users) ? $users[$role-&gt;id] : 0;
      $roles[] = $role;
    }
    $this-&gt;view-&gt;roles = $roles;
  }

  private function _getRefUsers()
  {
    $table = new DbTable_Users;
    $count = new Zend_Db_Expr('count(role)');
    $select = $table-&gt;select()-&gt;from($table, array('role', 'users' =&gt; $count))-&gt;group('role');
    $users = array();
    foreach ($table-&gt;fetchAll($select) as $row) {
      $users[$row-&gt;role] = $row-&gt;users;
    }
    return $users;
  }
}
</pre>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/870">Zend_Db_Selectで複数テーブルからデータを取得したいのだが…</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/870/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WordPressの引っ越しでハマったこと</title>
		<link>https://h2plus.biz/hiromitsu/entry/853</link>
					<comments>https://h2plus.biz/hiromitsu/entry/853#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Fri, 10 Oct 2014 23:18:36 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[サーバー移転]]></category>
		<category><![CDATA[共用サーバー]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=853</guid>

					<description><![CDATA[<p>本ブログをホストしているサーバー業者内でプランをアップデートしたのでWordPressを旧サーバーから新サーバーへ引っ越しをした。旧サーバーのMySQLのバージョンが古かったせいで、ずっとWordPressを3.1からア <a href='https://h2plus.biz/hiromitsu/entry/853' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/853">WordPressの引っ越しでハマったこと</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>本ブログをホストしているサーバー業者内でプランをアップデートしたのでWordPressを旧サーバーから新サーバーへ引っ越しをした。旧サーバーのMySQLのバージョンが古かったせいで、ずっとWordPressを3.1からアップデートできない状況だったが、新サーバーに引っ越したら最新バージョン（本エントリの執筆時点では4.0）にようやくアップデートできるようになる。ただし、今回はサーバー移転がメインなので、WordPress自体は同じバージョンのまま移行。</p>
<p>WordPressの引っ越しの手順はググればいくらでも見つかるが、</p>
<ol>
<li>旧サーバーでMySQLのバックアップ</li>
<li>旧サーバーでWordPressのバックアップ</li>
<li>MySQLのバックアップファイルを調整</li>
<li>WordPressのバックアップファイルを調整</li>
<li>新サーバーでMySQLのリストア</li>
<li>新サーバーでWordPressのリストア</li>
</ol>
<p>というのが大まかな流れ。</p>
<p><span id="more-853"></span><br />
今回の引っ越しのポイントとしては</p>
<ul>
<li>ブログのURLは同じ</li>
<li>WordPressがインストールされている物理パスが変わる</li>
</ul>
<p>という2点。</p>
<p>共用サーバーを使っていると、ホームディレクトリのパスはサーバー業者に割り当てられてしまう宿命なので、2点目のWordPressのインストールパスが変わるというのはよくあることだろう。新旧サーバーで同じホームディレクトリだったら簡単なんだけどねぇ。</p>
<p>ここでは新旧サーバーでのWordPressのインストールパスを</p>
<ul>
<li>旧サーバー：<code>/home/before/public_html/wordpress</code></li>
<li>新サーバー：<code>/home/after/public_html/wordpress</code></li>
</ul>
<p>と仮定しましょう。</p>
<p>MySQLのバックアップファイル（<code>mysqldump</code>で出力されたSQLスクリプト）には、データの一部に物理パスが含まれているので、そいつらを手で全部書き換えてやらなきゃいけない。最初は単純な文字列置換だから<code>sed</code>で一括変換すればいいやと思ってたんだけど、いざリストアしてみるとプラグインのいくつかが動かなくなりハマったわけ。</p>
<p>改めてバックアップしたSQLスクリプトから物理パスを抽出してみると、物理パスが書かれているのはすべて<code>wp_options</code>テーブルの中で、</p>
<pre>
s:83:\"/home/before/public_html/wordpress/wp-content/plugins/wp-dbmanager/wp-dbmanager.php\"
</pre>
<p>というレコードになっていた。</p>
<p>この<code>s:83</code>って部分が何を意味するのかわからなかったけど、後続の文字列数を数えてみると83文字！他にも<code>i:4</code>とか<code>a:2</code>とかがあるところを見ると、文字列は<code>s:文字列長:"値"</code>、数値は<code>i:値</code>、配列は<code>a:要素数:{値}</code>というルールっぽい。たぶん、WordPressのAPIがシリアライズしてくれてるんだな。</p>
<p>つまり、物理パスを書き換える場合、単純な文字列置換だけじゃなく文字列長を正しく調整する必要があると。新旧サーバーで物理パスの文字列の差分は「<code>before</code>」と「<code>after</code>」で、文字列長は1つ減ることになる。</p>
<p>前述のレコードだと</p>
<pre>
s:82:\"/home/after/public_html/wordpress/wp-content/plugins/wp-dbmanager/wp-dbmanager.php\"
</pre>
<p>と書き換えてやればいいわけだ。</p>
<p>コマンドで一括変換できないかとも思ったが、この形式でデータを保存しているプラグインの数が少なかったので、エディタでちまちま調整していった。</p>
<p>WordPressがMySQLに保存する物理パスをインストールパスからの相対パスになるように設計されていれば、こんな面倒なことはしなくて済むんだけど、これだけ世界中で使われているソフトだと現実的に途中で仕様変更はできないんだろうな。</p>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/853">WordPressの引っ越しでハマったこと</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/853/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>プロセスのコールスタックを調べるシェルスクリプト</title>
		<link>https://h2plus.biz/hiromitsu/entry/838</link>
					<comments>https://h2plus.biz/hiromitsu/entry/838#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Fri, 14 Feb 2014 22:30:58 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[シェルスクリプト]]></category>
		<category><![CDATA[cmdline]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[ifup]]></category>
		<category><![CDATA[PID]]></category>
		<category><![CDATA[PPID]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=838</guid>

					<description><![CDATA[<p>自分の知らないところで、あるコマンドが実行されているということはLinuxでは珍しいことではない。代表的なのはcronだ。ただ、cronならcrontab -lをしたり、直接/var/spool/cron/crontab <a href='https://h2plus.biz/hiromitsu/entry/838' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/838">プロセスのコールスタックを調べるシェルスクリプト</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>自分の知らないところで、あるコマンドが実行されているということはLinuxでは珍しいことではない。代表的なのは<code>cron</code>だ。ただ、<code>cron</code>なら<code>crontab -l</code>をしたり、直接<code>/var/spool/cron/crontabs/*</code>を調べれば、どんなコマンドが実行されるかすぐに調べがつく。しかし、あるイベントが発生したことをトリガーにしてコマンドが実行された場合、なぜそのコマンドが突然呼ばれたのかということに困惑することがある。</p>
<p>例えば、<code>ifup</code>コマンドが実行された場合、<code>/etc/network/if-pre-up.d</code>と<code>/etc/network/if-up.d</code>配下にあるコマンドが、<code>ifup</code>の実行中に呼び出されるようになっている。あるコマンドが実行されていることはログなどからわかっているが、どのような経緯で実行されるに至ったかがわからない、という場合には、そのコマンドの親プロセスを辿っていけば調べることができる。</p>
<p><span id="more-838"></span><br />
実行されているコマンドがバイナリだと手も足も出ないかもしれないが、シェルスクリプトであれば、以下のようなスクリプトを使って呼び出し元の親プロセスたちを辿ることができる。</p>
<pre class="brush: bash; title: ; notranslate">
trace_parents()
{
    local pid=$$ cmd
    while true; do
        cmd=&quot;$(cat /proc/$pid/cmdline | tr '&#92;&#48;' ' ')&quot;
        echo -e &quot;pid=$pid, cmd=$cmd&quot;
        [ $pid -eq 1 ] &amp;&amp; break
        pid=$(awk '/^PPid:/{print $2}' /proc/$pid/status)
    done
}

trace_parents &gt; /tmp/parents.$(basename $0).log
</pre>
<p>PIDが1、すなわち<code>init</code>になるまで<code>/proc/<em>pid</em>/status</code>の中から親プロセスのPIDを辿っていくというものだ。<code>/proc/<em>pid</em>/cmdline</code>を<code>cat</code>でダンプさせると、コマンドライン引数の間にスペースがないように見えるが、区切り文字はnull文字（<code>\0</code>）になっているので、<code>tr</code>でスペースに置換すれば見やすくなる。</p>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/838">プロセスのコールスタックを調べるシェルスクリプト</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/838/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SSHのパスフレーズ入力を省略する方法</title>
		<link>https://h2plus.biz/hiromitsu/entry/791</link>
					<comments>https://h2plus.biz/hiromitsu/entry/791#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Thu, 23 May 2013 05:53:41 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[シェルスクリプト]]></category>
		<category><![CDATA[bashrc]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[ssh-add]]></category>
		<category><![CDATA[ssh-agent]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=791</guid>

					<description><![CDATA[<p>公開鍵／秘密鍵を使ってリモートのSSHサーバーにアクセスする場合、秘密鍵にアクセスするためのパスフレーズを聞かれるようになる。Xが立ち上がっている端末であれば、初回の秘密鍵アクセス時にのみパスフレーズを聞かれるのだが、例 <a href='https://h2plus.biz/hiromitsu/entry/791' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/791">SSHのパスフレーズ入力を省略する方法</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>公開鍵／秘密鍵を使ってリモートのSSHサーバーにアクセスする場合、秘密鍵にアクセスするためのパスフレーズを聞かれるようになる。Xが立ち上がっている端末であれば、初回の秘密鍵アクセス時にのみパスフレーズを聞かれるのだが、例えばリモートのSSHサーバーから更に別のSSHサーバーにアクセスしたい場合は、SSHで接続する都度パスフレーズを聞かれて面倒になることがある。</p>
<p>今使っている環境では、メインのLinuxマシンの上で仮想マシン（Linux）を動かしていて、仮想マシンにSSHで入って開発をしている。gitリポジトリはまた別のサーバーにあって、そこにもSSHでつなぐ必要がある。リモートのgitリポジトリにアクセスする度に、パスフレーズを聞かれるのがウザかったので、仮想マシンにSSHでログインした後、手動で<code>ssh-agent</code>を立ち上げて<code>ssh-add</code>で秘密鍵を登録（このときパスフレーズを聞かれる）していたのだが、この手作業自体がまた面倒になった。</p>
<p><span id="more-791"></span><br />
メインマシン上でターミナルをいくつも立ち上げて、それぞれで仮想マシンにSSHログインしていると、仮想マシン上で<code>ssh-agent</code>がポンポン立ち上がることになる。さらに、仮想マシン上で別のシェルを起動させると、また<code>ssh-agent/ssh-add</code>を繰り返さなければならない。パスフレーズの入力は、仮想マシンに初めてログインしたときだけにしたいものだ。</p>
<p><code>ssh-agent</code>を起動する場合に、<code>ssh-agent bash</code>を実行するサンプルをよく見かけるが、これだと子プロセスとして別の<code>bash</code>が立ち上がることになり、仮想マシンからログアウトするのに<code>exit</code>を2回たたく羽目になる。</p>
<pre>
local$ ssh remote
remote$ ssh-agent bash
remote$ exit <span style="color:blue"># ssh-agent経由で起動したbashを抜けただけ</span>
remote$ exit <span style="color:blue"># ここでSSHから抜ける </span>
local$
</pre>
<p><code>ssh-agent</code>は引数なしで起動すると、<code>ssh-agent</code>に接続するために必要な環境変数を設定するためのスクリプトを吐き出してくれる。</p>
<pre>
$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-lryfV7tY5VHY/agent.26301; export SSH_AUTH_SOCK;
SSH_AGENT_PID=26302; export SSH_AGENT_PID;
echo Agent pid 26302;
</pre>
<p>手動で<code>ssh-agent</code>を起動する場合は、<code>eval "$(ssh-agent)"</code>とするのがいいだろう。</p>
<p>もったいぶってしまったが、<code>~/.bashrc</code>に以下のコードを追記すると、初回ログイン時にだけパスフレーズを聞かれるようになり、仮想マシン上で<code>ssh-agent</code>が複数立ち上がることもなくなる。</p>
<pre class="brush: bash; title: ; notranslate">
# Setup ssh-agent
if [ -f ~/.ssh-agent ]; then
    . ~/.ssh-agent
fi
if [ -z &quot;$SSH_AGENT_PID&quot; ] || ! kill -0 $SSH_AGENT_PID; then
    ssh-agent &gt; ~/.ssh-agent
    . ~/.ssh-agent
fi
ssh-add -l &gt;&amp; /dev/null || ssh-add
</pre>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/791">SSHのパスフレーズ入力を省略する方法</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/791/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>exprestをnpmにリリース</title>
		<link>https://h2plus.biz/hiromitsu/entry/765</link>
					<comments>https://h2plus.biz/hiromitsu/entry/765#comments</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Mon, 15 Oct 2012 05:49:14 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<category><![CDATA[express]]></category>
		<category><![CDATA[express-resource]]></category>
		<category><![CDATA[exprest]]></category>
		<category><![CDATA[NPM]]></category>
		<category><![CDATA[REST]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=765</guid>

					<description><![CDATA[<p>express-resourceを使って効率よくREST APIを実装できるようにするためのexprestというnodeパッケージをNPMで公開してみた。と言っても、リリースしたのは2ヶ月も前の話。使い方などはREADM <a href='https://h2plus.biz/hiromitsu/entry/765' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/765">exprestをnpmにリリース</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><a href="https://github.com/expressjs/express-resource">express-resource</a>を使って効率よくREST APIを実装できるようにするためのexprestというnodeパッケージをNPMで公開してみた。と言っても、リリースしたのは2ヶ月も前の話。使い方などはREADME.mdに英語で書いておいたが、英語が苦手な人のために日本語の対訳を書いておこう。</p>
<p><span id="more-765"></span></p>
<h4>インストール</h4>
<p>通常のNPMパッケージと同じように<code>npm install</code>でインストール。</p>
<pre>
$ npm install exprest
</pre>
<h4>使用方法</h4>
<p><code>exprest</code>が提供するのは<code>configure()</code>という関数のみ。</p>
<pre>
exprest.configure(app [,opts]);
</pre>
<p><code>app</code>には、<code>express.HTTPServer</code>オブジェクトか<code>express.HTTPSServer</code>を渡すように。もちろん、<code>exprest#configure()</code>を呼ぶ前に<code>express-resource</code>を<code>require()</code>で読み込んでおくこと。</p>
<p>一番シンプルな使い方はこんな感じ。</p>
<pre class="brush: jscript; title: ; notranslate">
var express = require('express')
  , resource = require('express-resource')
  , exprest = require('exprest')
  , app = express.createServer();

exprest.configure(app);
</pre>
<p>ルーティングのコードは一切書く必要がない。アプリケーションのルートディレクトリに<code>resources</code>というディレクトリを作って、そこに以下のような<code>resource</code>モジュールを実装しておけば、ルーティングは<code>exprest</code>が面倒を見てくれるようになっている。</p>
<pre class="brush: jscript; title: ; notranslate">
module.exports = {
    index: function(req, res) {
      res.end('index');
    }
  , new: function(req, res) {
      res.end('new');
    }
  , create: function(req, res) {
      res.end('create');
    }
  , show: function(req, res) {
      res.end('show');
    }
  , edit: function(req, res) {
      res.end('edit');
    }
  , update: function(req, res) {
      res.end('update');
    }
  , destroy: function(req, res) {
      res.end('destroy');
    }
};
</pre>
<p><code>resource</code>モジュールのファイル名がREST APIで提供するリソースへのパスを表すようになっている。例えば、<code>resource/users.js</code>というモジュールを実装したら、<code>exprest</code>が<code>/users/*</code>に対するリクエストをルーティングしてくれる。</p>
<h4>オプション</h4>
<p><code>configure()</code>関数の第2引数<code>opts</code>を使って、<code>exprest</code>の振る舞いをカスタマイズすることができる。<code>opts</code>を省略した場合は、以下のデフォルト値が適用される。</p>
<pre class="brush: jscript; title: ; notranslate">
{
    basedir: 'resources'
  , root: '/'
  , vars: {}
  , varsKey: 'vars'
  , checkAuth: undefined
  , defaults: {}
}
</pre>
<ul>
<li><strong>basedir</strong>（文字列）<br />
<code>resource</code>モジュールの配置先となるディレクトリ。
</li>
<li><strong>root</strong>（文字列）<br />
REST APIのルートとなる仮想ディレクトリ。<br />
例えば、<code>{ root: '/api' }</code>を渡すと<code>/api/users/*</code>へのリクエストは<code>resources/users.js</code>にマップされる。
</li>
<li><strong>vars</strong>（オブジェクト）<br />
<a href="https://nodejs.org/api/http.html#http_class_http_serverrequest">http.ServerRequest</a>を介してAPIに渡す変数。<br />
このオプションにより、<code>resource</code>モジュール間で変数を共有することができる。例えば、<code>app.js</code>内で<code>exprest#configure()</code>を呼び出すコードがあって、<code>app.js</code>のローカル変数を<code>resource</code>モジュールに引き渡したかったとしよう。グローバル変数（<code>GLOBAL</code>）を使うという手もあるが、どのようなプログラムでもグローバル変数はできるだけ避けたいものである。<code>vars</code>オプションは、このような変数をhttp.ServerRequest経由で引き渡すためのものだ。</p>
<p>サンプルコードを見るのがわかりやすいだろう。</p>
<pre class="brush: jscript; title: ; notranslate">
// app.js
var express = require('express')
  , resource = require('express-resource')
  , exprest = require('exprest')
  , mysql = require('mysql').createConnection(...)
  , app = express.createServer();

exprest.configure(app, {
    vars: { mysql: mysql }
});

// resources/users.js
module.exports = {
    index: function(req, res) {
      var mysql = req.vars.mysql; // Passed by exprest
      mysql.query('SELECT * FROM users', function(err, users) {
          ...
      });
    }
};
</pre>
<p><code>resource</code>モジュール（サンプルでは<code>users</code>モジュール）がDB操作を伴うような場合、DB接続を保持するオブジェクト（サンプルでは<code>app.js</code>内の<code>mysql</code>オブジェクト）をすべての<code>resource</code>モジュールで共有したいケースはよくある。<code>resource</code>モジュールは<code>app.js</code>とは別ファイルに実装されているので、<code>mysql</code>オブジェクトを<code>app.js</code>から直接的に<code>resource</code>モジュールに引き渡すことはできないが、<code>exprest.configure()</code>で<code>vars</code>オプションにオブジェクトを引き渡すと、<code>resource</code>モジュールの各アクションメソッドの引数<code>req.vars</code>を介してアクセスすることができるようになる。
</li>
<li><strong>varsKey</strong>（文字列）<br />
<code>vars</code>で渡したオブジェクトを、http.ServerRequestにマップする際のキーを指定する。例えば、<code>varsKey</code>に<code>"hoge"</code>を指定した場合、<code>resource</code>モジュールの各アクションメソッドでは、<code>req.hoge</code>を介してアクセスすることになる。
</li>
<li><strong>checkAuth</strong>（関数）<br />
<code>checkAuth</code>オプションを指定すると、exprestはルーティングに先立って<code>checkAuth</code>に渡された関数を呼び出すようになる。これにより、APIに対する認証フィルターを設けることができる。<code>checkAuth</code>で指定する関数は、現在のセッションが認証済みかどうかを示す真偽値を返すように実装しなければならない。</p>
<p><code>checkAuth</code>が<code>true</code>を返した場合、exprestはそのままAPIにルーティングする。<code>false</code>を返した場合、exprestは何もしない。<code>checkAuth</code>は<code>false</code>を返す前に、クライアントにレスポンスを送らなければならない。</p>
<p>以下はセッションを用いて認証状態を管理するサンプル。</p>
<pre class="brush: jscript; title: ; notranslate">
var express = require('express')
  , resource = require('express-resource')
  , exprest = require('exprest')
  , app = express.createServer();

exprest.configure(app, {
    checkAuth: function(req, res) {
      if(req.session.user_id) {
        return true;
      }
      res.send(403);
      return false;
    }
});
</pre>
</li>
<li><strong>defaults</strong>（オブジェクト）<br />
express-resourceに引き渡すオプション。
</li>
</ul>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/765">exprestをnpmにリリース</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/765/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Sequelizeを使ってユーザー認証／実装編</title>
		<link>https://h2plus.biz/hiromitsu/entry/746</link>
					<comments>https://h2plus.biz/hiromitsu/entry/746#comments</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Sun, 26 Aug 2012 07:23:58 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<category><![CDATA[Sequelize]]></category>
		<category><![CDATA[サインアップ]]></category>
		<category><![CDATA[ユーザー認証]]></category>
		<category><![CDATA[ログアウト]]></category>
		<category><![CDATA[ログイン]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=746</guid>

					<description><![CDATA[<p>設計編からだいぶ間があいてしまったが、routes/auth.jsに枠組みだけ作ったauthモジュールの実装を埋めていこう。 スタート地点はこの段階。 サインアップ signupメソッドの役割は新規ユーザーを登録すること <a href='https://h2plus.biz/hiromitsu/entry/746' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/746">Sequelizeを使ってユーザー認証／実装編</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><a href="http://h2plus.biz/hiromitsu/entry/694">設計編</a>からだいぶ間があいてしまったが、<code>routes/auth.js</code>に枠組みだけ作った<code>auth</code>モジュールの実装を埋めていこう。</p>
<p>スタート地点はこの段階。</p>
<pre class="brush: jscript; title: ; notranslate">
module.exports = function(models) {
  var User = models.user;

  return {
      signup: function(req, res) {
      }

    , login: function(req, res) {
      }

    , logout: function(req, res) {
      }
  };
};
</pre>
<p><span id="more-746"></span></p>
<h4>サインアップ</h4>
<p><code>signup</code>メソッドの役割は新規ユーザーを登録すること。リクエストボディから<code>username</code>と<code>password</code>の2つのパラメータを読み取って、<code>User</code>モデルを介して DBにレコードを追加する。</p>
<pre class="brush: jscript; title: ; notranslate">
signup: function(req, res) {
  var username = req.body.username || null
    , password = req.body.password || null;

  if(!username || !password) {
    res.statusCode = 403;
    res.end({ error: 'Shortage of params' });
    return;
  }

  User.create({
      username: username
    , password: User.hashPassword(password)
  })
  .success(function(user) {
    user = user.values;
    delete user.password;
    res.end(user);
  })
  .error(function(error) {
    res.statusCode = 500;
    res.end({ error: error });
  });
}
</pre>
<p>11-14行目：DBにレコードを追加するにはモデルの<code>create</code>メソッドを使う。引数は新しいレコードのデータを表すオブジェクトリテラル。リクエストボディから読み取った<code>password</code>は平文なので、<code>User</code>モデルのクラスメソッドとして実装した<code>hashPassword</code>メソッドを使ってハッシュ値に変換している。</p>
<p>15-19行目：レコードの追加が成功すると<code>success</code>メソッドに渡した関数がコールバックされ、追加されたモデルのインスタンスが引数に渡される。追加されたレコードにアクセスするには<code>values</code>プロパティを使う。例えば、自動連番のインデックスや、現在時刻がデフォルト値になっている時刻データのように、追加時に確定するようなデータも<code>values</code>プロパティには含まれている。<code>password</code>カラムはハッシュ化されたパスワードだが、外部に漏らすことはないので、クライアントには送信しないようにしている。</p>
<p>20-23行目：<code>error</code>メソッドにはレコードの追加に失敗した場合のコールバック（エラーハンドラ）を渡す。</p>
<p>レコードの追加は<code>create</code>メソッドを使う以外に、<code>build</code>メソッドと<code>save</code>メソッドを別々に呼び出す方法もある。データの追加前にバリデーションを行うのがよくあるケースだろう。例えば、<code>username</code>カラムをメールアドレス形式にするように仕様を変えたとした場合、<code>User</code>モデルの定義時にバリデーションも含めるようにする。</p>
<pre class="brush: jscript; highlight: [7]; title: ; notranslate">
// models/user.js
module.exports = function(sequelize, DataTypes) {
    return sequelize.define('user', {
        username: {
            type: DataTypes.STRING
          , unique: true
          , validate: { isEmail: true }
        }
      , password: 'char(40)'
    });
};
</pre>
<p>データ追加前にバリデーションを行うには、先に<code>build</code>メソッドでモデルのインスタンスを生成して<code>validate</code>メソッドを呼び出す。</p>
<pre class="brush: jscript; first-line: 11; title: ; notranslate">
  var new_user = User.build({
          username: username
        , password: User.hashPassword(password)
      })
    , invalid = new_user.validate();

  if(invalid) {
    res.statusCode = 403;
    res.end({ error: 'Invalid param', detail: invalid });
    return;
  }

  new_user.save()
  .success(function(user) {
    ...
  })
  .error(function(error) {
    ...
  });
</pre>
<p><code>validate</code>メソッドは、モデル定義の<code>validate</code>で指定したルールを使ってバリデーションを行い、何もエラーがなければ<code>null</code>が返ってくる。<a href="http://docs.sequelizejs.com/#models-validations">Sequelizeに標準で組み込まれているバリデータ</a>がたくさんあるので見ておくといいだろう。</p>
<p>すでに<a href="http://h2plus.biz/hiromitsu/entry/694">設計編</a>でルーティングは実装しているので、<code>curl</code>を使ってサインアップを実行してみよう。</p>
<pre>
$ curl -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/signup
</pre>
<h4>ログイン</h4>
<p><code>login</code>メソッドの役割はユーザーを認証すること。リクエストボディから<code>username</code>と<code>password</code>の2つのパラメータを読み取って、<code>User</code>モデルを介して DBからレコードを検索する。</p>
<pre class="brush: jscript; title: ; notranslate">
login: function(req, res) {
  var username = req.body.username || null
    , password = req.body.password || null;

  if(!username || !password) {
    res.statusCode = 403;
    res.end({ error: 'Shortage of params' });
    return;
  }

  User.find({
      where: {
          username: username
        , password: User.hashPassword(password)
      }
  })
  .success(function(user) {
    if(user) {
      user = user.values;
      delete user.password;
      req.session.user_id = user.id;
      res.send(user);
    }
    else {
      res.statusCode = 403;
      res.end({ error: 'Authentication failure' });
    }
  })
  .error(function(error) {
    res.statusCode = 500;
    res.end({ error: error });
  });
}
</pre>
<p>11-16行目：1件のレコードを検索するには<code>find</code>メソッドを使う。引数には検索（<code>SELECT</code>文）の条件などを渡すことができる。今回はユーザー認証が目的なので、ユーザー名とパスワードのハッシュ値を<code>WHERE</code>句に指定して検索している。複数件のレコードを検索する場合は、<code>findAll</code>（または<code>all</code>）メソッドを使う。</p>
<p>18-23行目：検索を実行した結果、レコードが見つかった場合は、該当レコードのインスタンスがコールバック関数の引数に渡ってくる。サインアップで使った<code>create</code>と同様にデータへのアクセスは<code>values</code>プロパティを使う。レコードが見つかったということは、ユーザー名とパスワードが一致した証なので、セッションに<code>id</code>カラムを保存しておくようにする。</p>
<p>24-27行目：検索の結果、レコードが見つからなかった場合も<code>success</code>メソッドに渡したコールバック関数が呼び出されるが、引数は<code>null</code>になっている。<code>error</code>メソッド側のコールバックが呼ばれるわけではないので注意。</p>
<p>先ほどサインアップしたアカウントを使って、<code>curl</code>からログインを実行してみる。セッション管理にはCookieが使われているので、<code>-c</code>オプションでCookieJarに保存するようにしておこう。</p>
<pre>
$ curl -c ~/.cookies -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/login
</pre>
<h4>ログアウト</h4>
<p><code>logout</code>メソッドの役割は既存のログインセッションを破棄すること。サインアップ、ログインに比べると、DBアクセスもないので簡素な実装になる。</p>
<pre class="brush: jscript; title: ; notranslate">
logout: function(req, res) {
  req.session.destroy();
  res.end();
}
</pre>
<p><code>curl</code>でのログアウト実行時は、ログイン時と同じCookieJarを<code>-b</code>オプションで指定する。</p>
<pre>
$ curl -b ~/.cookies -X POST localhost:3000/auth/logout
</pre>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/746">Sequelizeを使ってユーザー認証／実装編</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/746/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>boost::property_treeで日本語を含むJSONが文字化けする</title>
		<link>https://h2plus.biz/hiromitsu/entry/728</link>
					<comments>https://h2plus.biz/hiromitsu/entry/728#respond</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Mon, 06 Aug 2012 01:29:05 +0000</pubDate>
				<category><![CDATA[boost]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[json_encode]]></category>
		<category><![CDATA[property_tree]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[UTF-8]]></category>
		<category><![CDATA[write_json]]></category>
		<category><![CDATA[wstring]]></category>
		<category><![CDATA[マルチバイト]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=728</guid>

					<description><![CDATA[<p>久しぶりにC++ネタ。JSONを生成するプログラムをC++で書こうなんて人がそもそもいない気もするが、boost::property_tree::write_json()を使うと簡単にJSON出力ができる。 コマンドライ <a href='https://h2plus.biz/hiromitsu/entry/728' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/728">boost::property_treeで日本語を含むJSONが文字化けする</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>久しぶりにC++ネタ。JSONを生成するプログラムをC++で書こうなんて人がそもそもいない気もするが、<a href="https://www.boost.org/doc/libs/1_50_0/doc/html/property_tree/reference.html#header.boost.property_tree.json_parser_hpp"><code>boost::property_tree::write_json()</code></a>を使うと簡単にJSON出力ができる。</p>
<pre class="brush: cpp; title: ; notranslate">
#include &amp;amp;lt;iostream&amp;amp;gt;
#include &amp;amp;lt;sstream&amp;amp;gt;
#include &amp;amp;lt;boost/property_tree/json_parser.hpp&amp;amp;gt;
using namespace std;

int main(int argc, char *argv[])
{
	if(argc &amp;amp;lt; 2)
	{
		cerr &amp;amp;lt;&amp;amp;lt; &amp;amp;quot;Usage: &amp;amp;quot; &amp;amp;lt;&amp;amp;lt; argv[0] &amp;amp;lt;&amp;amp;lt; &amp;amp;quot; message&amp;amp;quot; &amp;amp;lt;&amp;amp;lt; endl;
		return 1;
	}

	boost::property_tree::ptree pt;
	pt.put(&amp;amp;quot;message&amp;amp;quot;, argv[1]);
	boost::property_tree::write_json(cout, pt, false);
	return 0;
}
</pre>
<p><span id="more-728"></span><br />
コマンドライン引数に<code>hello</code>を渡して出力されたJSONがこちら。</p>
<pre>
$ g++ -I/usr/include/boost_1_50_0 -o write_json main.cpp
$ ./write_json hello
{"message":"hello"}
</pre>
<p>では引数に日本語を渡すとどうなるか。</p>
<pre>
$ ./write_json ほげ
{"message":"\u00E3\u0081\u00BB\u00E3\u0081\u0092"}
</pre>
<p>シェル実行環境のロケールは<code>en_US.UTF-8</code>。日本語部分は1バイトずつ<code>\u00XX</code>という形にエンコードされている。</p>
<p>このJSONをChrome Development Toolで読み込ませてみる。<br />
<img src="http://h2plus.biz/hiromitsu/wp-content/uploads/2012/08/GarbledJSON.gif" alt="GarbledJSON" title="文字化け発生" class="size-full wp-image-731" /><br />
素敵に文字化け。</p>
<p><a href="http://www.json.org/">JSONの仕様</a>で文字列型は</p>
<blockquote><p>A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. </p></blockquote>
<p>と定義されているので、UTF-8から一旦Unicodeに変換しなければならない。<a href="http://php.net/manual/en/function.json-encode.php">PHPの<code>json_encode()</code></a>なんかは、入力の文字コードがUTF-8に限定しているものの、内部でUnicode化してからJSONエンコードをしてくれているようだ。</p>
<p>ということで、まずはUTF-8からUnicodeへ変換する処理が必要になる。また、マルチバイト文字列（<code>std::string</code>）とワイド文字列（<code>std::wstring</code>）との変換もしなければならない。面倒ではあったが、標準ライブラリの<code>codecvt</code>を使えばなんとかなりそうだったので、UTF-8からUnicodeへの変換を行う<code>widen()</code>関数と、その逆変換を行う<code>narrow()</code>関数（JSON出力の目的ではなくてもいいんだけど対称性のため）を提供する<code>CodeCvt</code>クラスを作ってみた。</p>
<pre class="brush: cpp; title: ; notranslate">
#include &amp;amp;lt;iostream&amp;amp;gt;
#include &amp;amp;lt;stdexcept&amp;amp;gt;
#include &amp;amp;lt;vector&amp;amp;gt;
#include &amp;amp;lt;string&amp;amp;gt;
#include &amp;amp;lt;cstring&amp;amp;gt;
#include &amp;amp;lt;locale&amp;amp;gt;
#include &amp;amp;lt;boost/property_tree/json_parser.hpp&amp;amp;gt;
using namespace std;

class CodeCvt
{
public:
	explicit CodeCvt(const char *locale=&amp;amp;quot;en_US.UTF-8&amp;amp;quot;)
	: locale_(locale), codecvt_(use_facet&amp;amp;lt;codecvt_type&amp;amp;gt;(locale_)) {}

	wstring widen(const string &amp;amp;amp;s)
	{
		const char *fb = s.data();
		const char *fe = fb+s.size();
		const char *fn;
		vector&amp;amp;lt;wchar_t&amp;amp;gt; ws(s.size());
		wchar_t *tb = &amp;amp;amp;ws.front();
		wchar_t *te = tb+ws.size();
		wchar_t *tn;
		mbstate_t st;
		bzero(&amp;amp;amp;st, sizeof(st));

		const codecvt_base::result res = codecvt_.in(st, fb, fe, fn, tb, te, tn);
		assert_result(res, fn, fe);
		return wstring(tb, tn);
	}

	string narrow(const wstring &amp;amp;amp;ws)
	{
		const wchar_t *fb = ws.data();
		const wchar_t *fe = fb+ws.size();
		const wchar_t *fn;
		vector&amp;amp;lt;char&amp;amp;gt; s(ws.size()*4);
		char *tb = &amp;amp;amp;s.front();
		char *te = tb+s.size();
		char *tn;
		mbstate_t st;
		bzero(&amp;amp;amp;st, sizeof(st));

		const codecvt_base::result res = codecvt_.out(st, fb, fe, fn, tb, te, tn);
		assert_result(res, fn, fe);
		return string(tb, tn);
	}

private:
	template&amp;amp;lt;class CharT&amp;amp;gt;
	static void assert_result(codecvt_base::result res, const CharT *fn, const CharT *fe)
	{
		switch(res)
		{
			case codecvt_base::ok:
				if(fn != fe)
				{
					throw invalid_argument(&amp;amp;quot;Conversion failure&amp;amp;quot;);
				}
				break;
			case codecvt_base::partial:
				throw invalid_argument(&amp;amp;quot;partial&amp;amp;quot;);
			case codecvt_base::error:
				throw invalid_argument(&amp;amp;quot;error&amp;amp;quot;);
			case codecvt_base::noconv:
				throw invalid_argument(&amp;amp;quot;noconv&amp;amp;quot;);
		}
	}

	typedef codecvt&amp;amp;lt;wchar_t, char, mbstate_t&amp;amp;gt; codecvt_type;
	const locale locale_;
	const codecvt_type &amp;amp;amp;codecvt_;
};
</pre>
<p><code>mbstate_t</code>型の変数<code>st</code>を<code>bzero()</code>や<code>memset()</code>などで初期化しておかないと、<code>codecvt_.in()</code>や<code>codecvt_.out()</code>の中で不測の事態が起こるので注意（実際テスト中に無限ループにハマってデバッガで追いかける羽目になった）。</p>
<p><code>main()</code>関数側では、<code>ptree</code>の代わりに<code>wptree</code>を使い、出力も<code>cout</code>ではなく<code>wcout</code>を使うようにするだけ。</p>
<pre class="brush: cpp; title: ; notranslate">
int main(int argc, char *argv[])
{
	try
	{
		CodeCvt cvt;
		boost::property_tree::wptree pt;
		pt.put(L&amp;amp;quot;message&amp;amp;quot;, cvt.widen(argv[1]));
		boost::property_tree::write_json(wcout, pt, false);
		return 0;
	}
	catch(const exception &amp;amp;amp;e)
	{
		cerr &amp;amp;lt;&amp;amp;lt; e.what() &amp;amp;lt;&amp;amp;lt; endl;
		return 1;
	}
}
</pre>
<p>さっそく実行してみる。</p>
<pre>
$ ./write_json ほげ
{"message":"\u307B\u3052"}
</pre>
<p>ちゃんとUnicodeのバイトシーケンスでエンコードされているのがわかる。</p>
<p>同じようにChrome Development Toolで読み込ませてみる。<br />
<img loading="lazy" width="355" height="130" src="http://h2plus.biz/hiromitsu/wp-content/uploads/2012/08/FixedJSON.gif" alt="FixedJSON" title="文字化け解消" class="size-full wp-image-737" srcset="https://h2plus.biz/hiromitsu/wp-content/uploads/2012/08/FixedJSON.gif 355w, https://h2plus.biz/hiromitsu/wp-content/uploads/2012/08/FixedJSON-300x109.gif 300w" sizes="(max-width: 355px) 100vw, 355px" /></p>
<p>今度はちゃんと「ほげ」と表示されるようになった。</p>
<p><code>CodeCvt</code>クラスのコンストラクタ引数で別の文字コード（例えば<code>ja_JP.EUC-JP</code>）を渡せば、UTF-8以外のマルチバイト文字列からでもJSON化することができるはず。</p>
<p>ソースは<a href="https://gist.github.com/bow-fujita/3282693">Gist</a>に置いといた。</p>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/728">boost::property_treeで日本語を含むJSONが文字化けする</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/728/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Sequelizeを使ってユーザー認証／設計編</title>
		<link>https://h2plus.biz/hiromitsu/entry/694</link>
					<comments>https://h2plus.biz/hiromitsu/entry/694#comments</comments>
		
		<dc:creator><![CDATA[Hiro]]></dc:creator>
		<pubDate>Sun, 29 Jul 2012 05:10:41 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<category><![CDATA[Sequelize]]></category>
		<category><![CDATA[SHA-1]]></category>
		<category><![CDATA[サインアップ]]></category>
		<category><![CDATA[ユーザー認証]]></category>
		<category><![CDATA[ログアウト]]></category>
		<category><![CDATA[ログイン]]></category>
		<guid isPermaLink="false">http://h2plus.biz/hiromitsu/?p=694</guid>

					<description><![CDATA[<p>Sequelizeを使う準備が整ったところで、どんなWebアプリを作るにしても避けて通れないであろうユーザー認証の仕組みを実装していく。 大まかな流れはこんな感じ。 Userモデルを定義する 認証モジュールを設計する リ <a href='https://h2plus.biz/hiromitsu/entry/694' class='excerpt-more'>[...]</a></p>
<p>The post <a href="https://h2plus.biz/hiromitsu/entry/694">Sequelizeを使ってユーザー認証／設計編</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></description>
										<content:encoded><![CDATA[<p> <a href="http://h2plus.biz/hiromitsu/entry/659">Sequelizeを使う準備</a>が整ったところで、どんなWebアプリを作るにしても避けて通れないであろうユーザー認証の仕組みを実装していく。</p>
<p>大まかな流れはこんな感じ。</p>
<ol>
<li><code>User</code>モデルを定義する</li>
<li>認証モジュールを設計する</li>
<li>リクエストのルーティングを追加する</li>
<li>認証モジュールを実装する</li>
</ol>
<p><span id="more-694"></span><br />
はじめにアプリケーションのディレクトリ構造がどうなっているかおさらい。</p>
<pre>
myrest/
├── app.js
├── config/
│   └── development.json
├── libs/
│   ├── dbconn/
│   └── models/
├── models/
│   └── user.js
├── node_modules/
├── package.json
└── routes/
    ├── index.js
    └── sync.js
</pre>
<h4>Userモデルを定義する</h4>
<p><code>User</code>モデルは<code>models/user.js</code>で以下のように定義していた。</p>
<pre class="brush: jscript; title: ; notranslate">
module.exports = function(sequelize, DataTypes) {
    return sequelize.define('user', {
        username: DataTypes.STRING
      , password: 'char(40)'
    });
};
</pre>
<p>ユーザーを認証する際は<code>username</code>カラムがキーになるので、UNIQUE制約を追加しておく必要がある。<br />
<code>password</code>カラムは40バイトの固定長としているのは、SHA-1のハッシュ値（160bit）を16進数で格納するためだ。平文のパスワードからハッシュ値を計算する関数はいずれ必要になるので、<code>User</code>モデルのクラスメソッドとして実装しておく。</p>
<pre class="brush: jscript; highlight: [3,4,5,6,10,11,12,13,14,15,16]; title: ; notranslate">
module.exports = function(sequelize, DataTypes) {
    return sequelize.define('user', {
        username: {
            type: DataTypes.STRING
          , unique: true
        }
      , password: 'char(40)'
    }, // End of table definition
    {
        classMethods: {
            hashPassword: function(pass) {
                var sha1 = require('crypto').createHash('sha1');
                sha1.update(pass, 'utf8');
                return sha1.digest('hex');
            }
        }
    });
};
</pre>
<p>こうしておくことで、<code>User</code>モデルを参照できるところでは、<code>User.hashPassword()</code>を呼び出せるようになる。</p>
<h4>認証モジュールを設計する</h4>
<p>実装する認証機能はサインアップ、ログイン、ログアウトの基本的なものだけにしておく。facebook連携とかパスワードリマインダーとか、認証モジュールの仕事はたくさんあるけどね。</p>
<p>まず<code>routes/auth.js</code>に認証モジュールの枠組みだけ作ってしまおう。</p>
<pre class="brush: jscript; title: ; notranslate">
module.exports = function(models) {
  var User = models.user;

  return {
      signup: function(req, res) {
      }

    , login: function(req, res) {
      }

    , logout: function(req, res) {
      }
  };
};
</pre>
<p><code>routes/auth</code>モジュールは、<code>models</code>オブジェクトを引数に受け取り、認証機能を提供するオブジェクトを返す関数として実装する。</p>
<p><code>signup</code>メソッドで<code>users</code>テーブルに新規データを登録。<code>login</code>メソッドで認証処理を行いセッションを使ってログイン状態を保存する。<code>logout</code>メソッドはセッションの破棄を行うだけ。</p>
<p>各メソッドの中身は実装編にて。</p>
<h4>リクエストのルーティングを追加する</h4>
<p><code>routes/index.js</code>で<code>routes/auth</code>モジュールの各メソッドにルーティングするコードを追加する。</p>
<pre class="brush: jscript; highlight: [13,14,15,16,17]; title: ; notranslate">
module.exports = function(app, config) {

  var sequelize = require('dbconn')(config)
    , models = require('models')(sequelize);

  // Database migration
  app.configure('development', function() {
    var sync = require('./sync');
    app.post('/devel/sync/:table', sync.one(models));
    app.post('/devel/sync', sync.all(sequelize));
  });

  // Authentication
  var auth = require('./auth')(models);
  app.post('/auth/signup', auth.signup);
  app.post('/auth/login', auth.login);
  app.post('/auth/logout', auth.logout);

};
</pre>
<p>すべてPOSTメソッドのみルーティングして、入力となるデータはexpressが提供する<code>request.body</code>経由で取得するようになる。</p>
<p><a href="http://h2plus.biz/hiromitsu/entry/746">実装編</a>へ続く&#8230;</p>
 <!-- Easy Plugin for AdSense Unfiltered [count: 3 is not less than 3] --><p>The post <a href="https://h2plus.biz/hiromitsu/entry/694">Sequelizeを使ってユーザー認証／設計編</a> first appeared on <a href="https://h2plus.biz/hiromitsu">Inhale n' Exhale</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://h2plus.biz/hiromitsu/entry/694/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
