ASUSTOR AS1004T 購入

2016/3/16

アニメ録画サーバと、エンコード済み動画の保存用NASの空き容量が逼迫してきたため、新たにNASを購入しました。

既存のNASはNETGEAR ReadyNAS 102(RN10200)です。
低価格帯の製品だけあってCPU(Marvell Armada 370)が非力で、転送速度もそこそこといったところ。
とはいえ、自分の用途には十分な性能なので良い買い物ではありました。

今回も同じシリーズでReadyNAS 104を買おうかな、とも考えたのですが、せっかくなら新しい製品にしようと思い色々物色することに・・・。

Read more…

DELL XPS 13 9343 無線LANカード換装

2015/12/13

半年ほど前にXPS 13(9343)を購入したのですが、評判の通りとても良いマシンで気に入っています。
やっぱりある程度金出さないとダメなんだなあと思いました。
Skylakeが搭載された新しいモデル(9350)も販売されているらしいですね。

さて、今回は内蔵の無線LANカード(DELL  DW1560)をIntel Dual Band Wireless-AC 7265へ換装するお話です。
なぜ換装したかといえば、このPCにはFedoraをインストールして使っているのですが、どうもWiFI接続が不安定なのです。DELL DW1560の中身はBroadcom BCM94352Zなのですが、b43ではサポートされておらず、broadcom-wlを導入する必要があります。

当初は問題なく動作していると思いきや、度々全く接続できなくなったり、特定のAPに接続できなかったり、Bluetoothが動作しないなどの症状が出ていました。
有線LANが使えないので、WiFiが不安定なのは致命傷です。

Arch Linux WikiのDell XPS 13 (2015)ページを見ても、メインラインカーネルでサポートされているIntel 7265に換装した方が幸せになれそうということが分かります。
早速どこで買おうかと色々検索したのですが、国内よりAmazon.comの方が安いことが分かり日本円にして3,330円で購入できました。Amazon.co.jpにも3,000円前後のものがありますが、それはWireless-Nで802.11 acに対応していないモデルなので要注意です(偽物もあるかも)。

Amazon.com: Intel 7265NGW Dual Band 2×2 Wireless AC + Bluetooth 4.0 M2 Interface Also Supports 802.11 AC-B/G/N

注文から10日ほどで到着しました。それでは換装作業です。
例によってサービスマニュアルに分解から換装まで丁寧に記載されているので、それほど困りませんね。

Dell 製品サポート: XPS 13 9343 サービスマニュアル PDF (8748 KB)

p.12~の通り、裏面のベースカバーさえ外せてしまえば、あとは簡単です。
とは言いつつも、ネジを全て外した後、カバーのツメが非常に外しづらく、無理に力を入れれば折れます。(数カ所折れてしまいました・・・)
アルミ削り出しの筐体であの薄さですから、遊びも少なく少し歪ませることもできません。
図中にもありますが、ヒンジの側から何か差し込んで少しずつ外していく必要があります。
私はステンレス直定規でこじ開けつつ竹串を挟んで作業しましたが、プラスティック製のスクレーパーがあったほうが良いと思います。

XPS13_1

写真中央にあるのがSSDで、右上に「WLAN」と書かれているのが無線LANカードです。
ちゃちゃっと交換して、カバーを戻して、起動。

無事認識して、正常に動作していました!
以前あったような症状も一切なくなり、快適そのものです。
数千円のお金と多少の時間をかけてでも、やる価値はありますね。

FuelPHPのEmailパッケージをS/MIMEに対応させる

2015/9/28

FuelPHPのEmailパッケージをS/MIMEに対応させるお話です。(今回は電子署名のみ)

Emailパッケージを拡張するのには、以下のような方法で行いました。

#! /bin/blog: [FuelPHP]ネームスペースを上書きしてパッケージのクラスを書き換える

変更が必要なのは Email\Email_Driver だけなので、Emailパッケージから必要なファイルをコピーして以下のような構成を作ります。

fuel
└ packages
    └myemail
        ├─ bootstrap.php
        |─ classes
        |    └─ email
        |         └─ driver.php
        └─ config
             └─ email.php

bootstrap.php は以下のように指定して、コピーした方のファイルでEmail_Driverを上書きします。
また、fuel/app/config 以下の config.php のalways_load に myemail を追加するのも忘れずに。

<?php
\Autoloader::add_core_namespace('Email');

\Autoloader::add_classes(array(
	/**
	 * Email classes.
	 */
	'Email\\Email_Driver'                      => __DIR__.'/classes/email/driver.php',

));

これで下準備は完了です。それでは、コピーしたファイルに変更を加えていきましょう。
driver.php
は build_message メソッドを以下のように変更します。

	protected function build_message($no_bcc = false)
	{
		$newline = $this->config['newline'];
		$charset = $this->config['charset'];
		$encoding = $this->config['encoding'];

		$headers = '';
		$parts = array('Date', 'Return-Path', 'From', 'To', 'Cc', 'Bcc', 'Reply-To', 'Subject', 'Message-ID', 'X-Priority', 'X-Mailer', 'MIME-Version', 'Content-Type');
		$no_bcc and array_splice($parts, 5, 1);
		$this->config['smime']['enabled'] and array_splice($parts, -2, 2);
		
		foreach ($parts as $part)
		{
			$headers .= $this->get_header($part);
		}

		foreach ($this->extra_headers as $header => $value)
		{
			$headers .= $header.': '.$value.$newline;
		}

		$headers .= $newline;

		$body = '';

		if ($this->type === 'plain' or $this->type === 'html')
		{
			if ($this->config['smime']['enabled'])
			{
				$body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
				$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
			}
			$body .= $this->body;
		}
		else
		{
			if ($this->config['smime']['enabled'])
			{
				$body .= $this->get_header('Content-Type').$newline;
			}
			
			switch ($this->type)
			{
				case 'html_alt':
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->alt_body.$newline.$newline;
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->body.$newline.$newline;
					$body .= '--'.$this->boundaries[0].'--';
					break;
				case 'plain_attach':
				case 'html_attach':
				case 'html_inline':
					$body .= '--'.$this->boundaries[0].$newline;
					$text_type = (stripos($this->type, 'html') !== false) ? 'html' : 'plain';
					$body .= 'Content-Type: text/'.$text_type.'; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->body.$newline.$newline;
					$attach_type = (stripos($this->type, 'attach') !== false) ? 'attachment' : 'inline';
					$body .= $this->get_attachment_headers($attach_type, $this->boundaries[0]);
					$body .= '--'.$this->boundaries[0].'--';
					break;
				case 'html_alt_inline':
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: text/plain'.'; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->alt_body.$newline.$newline;
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: multipart/related;'.$newline."\tboundary=\"{$this->boundaries[1]}\"".$newline.$newline;
					$body .= '--'.$this->boundaries[1].$newline;
					$body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->body.$newline.$newline;
					$body .= $this->get_attachment_headers('inline', $this->boundaries[1]);
					$body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
					$body .= '--'.$this->boundaries[0].'--';
					break;
				case 'html_alt_attach':
				case 'html_inline_attach':
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: multipart/alternative;'.$newline."\t boundary=\"{$this->boundaries[1]}\"".$newline.$newline;
					if (stripos($this->type, 'alt') !== false)
					{
						$body .= '--'.$this->boundaries[1].$newline;
						$body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
						$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
						$body .= $this->alt_body.$newline.$newline;
					}
					$body .= '--'.$this->boundaries[1].$newline;
					$body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->body.$newline.$newline;
					if (stripos($this->type, 'inline') !== false)
					{
						$body .= $this->get_attachment_headers('inline', $this->boundaries[1]);
						$body .= $this->alt_body.$newline.$newline;
					}
					$body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
					$body .= $this->get_attachment_headers('attachment', $this->boundaries[0]);
					$body .= '--'.$this->boundaries[0].'--';
					break;
				case 'html_alt_inline_attach':
					$body .= '--'.$this->boundaries[0].$newline;
					$body .= 'Content-Type: multipart/alternative;'.$newline."\t boundary=\"{$this->boundaries[1]}\"".$newline.$newline;
					$body .= '--'.$this->boundaries[1].$newline;
					$body .= 'Content-Type: text/plain; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->alt_body.$newline.$newline;
					$body .= '--'.$this->boundaries[1].$newline;
					$body .= 'Content-Type: multipart/related;'.$newline."\t boundary=\"{$this->boundaries[2]}\"".$newline.$newline;
					$body .= '--'.$this->boundaries[2].$newline;
					$body .= 'Content-Type: text/html; charset="'.$charset.'"'.$newline;
					$body .= 'Content-Transfer-Encoding: '.$encoding.$newline.$newline;
					$body .= $this->body.$newline.$newline;
					$body .= $this->get_attachment_headers('inline', $this->boundaries[2]);
					$body .= $this->alt_body.$newline.$newline;
					$body .= '--'.$this->boundaries[2].'--'.$newline.$newline;
					$body .= '--'.$this->boundaries[1].'--'.$newline.$newline;
					$body .= $this->get_attachment_headers('attachment', $this->boundaries[0]);
					$body .= '--'.$this->boundaries[0].'--';
					break;
			}

		}
		
		if ($this->config['smime']['enabled'])
		{
			$tmp_dir = $this->config['smime']['tmp_dir'];
			$infilename = 'email_'.md5($body.microtime());
			$outfilename = $infilename.'_out';
			$signcert = 'file://'.$this->config['smime']['signcert'];
			$privkey = 'file://'.$this->config['smime']['privkey'];
			$headers = substr($headers, 0, -strlen($newline)*2);
			
			if ( ! is_writable($tmp_dir))
			{
				throw new \FuelException('Cannot write into directory "'.$tmp_dir.'"');
			}
			if ( ! is_readable($signcert) || ! is_readable($privkey))
			{
				throw new \FuelException('Cannot read certificate or key.');
			}
			
			if ($this->config['smime']['keypass'])
			{
				$privkey = array($privkey, $this->config['smime']['keypass']);
			}
			
			\File::create($tmp_dir, $infilename, $body);

			openssl_pkcs7_sign($tmp_dir.$infilename, $tmp_dir.$outfilename, $signcert, $privkey, array($headers));

			$body = \File::read($tmp_dir.$outfilename, true);

			\File::delete($tmp_dir.$infilename);
			\File::delete($tmp_dir.$outfilename);
			
			$headers = '';
			$body = strtr($body, array_fill_keys(array("\r\n", "\r", "\n"), $newline));
		}

		return array(
			'header' => $headers,
			'body' => $body,
		);
	}

config/email.php には、以下の設定を追加します。

	'defaults' => array(
		
		<省略>
		
		/**
		 * S/MIME
		 */
		'smime' => array(
			'enabled' => false,
			'tmp_dir' => APPPATH.'tmp/',
			'signcert' => '',
			'privkey' => '',
			'keypass' => '',
		),
	),

実際使うときは、もちろんこれを fuel/app/config以下にコピーして書き換えてください。
tmp_dir は、openssl_pkcs7_sign の入出力に必要なファイルが読み書きされます。
適切なパーミッションが与えられたディレクトリを指定する必要があります。

証明書と秘密鍵はsigncert, privkeyでファイルパスを指定します。PEM形式じゃないとダメっぽいです。
詳しことは「PHP: OpenSSL キー/証明書パラメータ」をご覧ください。

これで設定が正しければS/MIME電子署名メールが送れるはずです。
注意としては、Emailのドライバがmail, smtp, sendmailでないとうまくいかないはずです。
mailgunだとWeb API経由で送信しており、こちらで組み立てたメール本文がそのまま配送されないためです。
mailgunでもSMTP経由で送信することも可能ですので、smtpドライバを用いてmailgunのSMTPサーバーで設定を行いましょう。

多次元配列の要素を特定の値をキーとしてマージする

2015/8/25

FuelPHPのArrクラスでは、非常に便利な配列操作のヘルパー関数が用意されているのですが、ありそうでなかったので…

それから、記事タイトルが何言ってるのか正直自分もよく分からないので例を。

array(
    array(
        'group' => 'CV1',
        'name' => 'Miku'
    ),
    array(
        'group' => 'CV2',
        'name' => 'Rin'
    ),
    array(
        'group' => 'CV2',
        'name' => 'Len'
    )
);

のような入力を与えた時に、

array(
    'CV1' => array(
        array(
            'group' => 'CV1',
            'name' => 'Miku'
        )
    ),
    'CV2' => array(
        array(
            'group' => 'CV2',
            'name' => 'Rin'
        ),
        array(
            'group' => 'CV2',
            'name' => 'Len'
        )
    )
);

あるいは、

array(
    'CV1' => array(
        'Miku'
    ),
    'CV2' => array(
        'Rin',
        'Len'
    )
);

のような出力を得たい、ということです。あるキーの同じ値同士でまとめるような感覚でしょうか。

とりあえず以下のように書いてみました。

class Arr extends \Fuel\Core\Arr
{
	/**
	 * @param array  $assoc     変換する配列
	 * @param string $key_field keyとなる連想配列の添字
	 * @param string $val_field valueとなる連想配列の添字
	 * @return array
	 */
	public static function merge_assoc_key($assoc, $key_field, $val_field = null)
	{
		$output = array_fill_keys(array_column($assoc, $key_field, $key_field), array());

		foreach ($assoc as $elm)
		{
			array_push($output[$elm[$key_field]], $val_field ? $elm[$val_field] : $elm);
		}

		return $output;
	}
}

パフォーマンスがどうか全く分からんですけど、

Arr::merge_assoc_key($input, 'group');
Arr::merge_assoc_key($input, 'group', 'name');

みたいな感じで、期待した結果が得られます。

データベースから取ってきた配列をこんな感じで整形したいことがそこそこある気がします、よね?

書いてて思ったけど、PHPには豊富な配列操作の標準関数が用意されるけども、マニュアルを眺めてあーこれこれ、こんなんあったね、とかやってるあたりまだ未熟だなぁ…

Windows 10 x64 + AMD Radeon でNetBeansがクラッシュする

2015/8/24

昨日、Windows 8.1 x64 なメインPCをWindows 10 Pro x64へアップグレードしました。
不具合があると困ってしまう開発環境周りは事前に検証をしていたのですが、実際アップグレードしてみるとハードが絡んだ問題が発生してしまったので書いておきます。
ちなみに発生条件は他に同様の報告も見当たらず詳しく調査していないので、記事タイトルにあるもの以外の要因があるかもしれません。


対象のソフトウェアはNetBeans 8.0.2
正常に起動するものの、しばらく操作していると突然クラッシュしてしまう現象が発生しました。
特定のアクションで発生するわけでもなく、ランダムに発生する感じでした。

まず、検証を行った環境と異なる点といえば、JDKを8u60に更新したので、以前発生したJava SE 8u20の不具合のような問題があるのではないかと考え、8u51にダウングレードしてみたものの症状は解決せず。

JVMのログを見てみると、

# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ff9813122bf, pid=5428, tid=8140
#
# JRE version: Java(TM) SE Runtime Environment (8.0_60-b27) (build 1.8.0_60-b27)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [atig6txx.dll+0x122bf]

とあり、グラフィック周りが怪しげです。
確かに、検証用のPCではIntel HD Graphicsでしたが、メインPCはRadeon R7 250Xを搭載している点が異なっています。

さらにアップグレード後に、グラフィックドライバをCatalyst 15.7.1に更新しており、これが原因ではないか?
ということで、Windows 8.1の時と同じ、一つ古いCatalyst 15.7をインストールし直してみましたが、解決せず。
さらに古いCatalyst 14.12も試してみましたが、これもダメでした。

半分諦めながら、色々ググっていると、以下のようなページを発見。

Chatty Help – Troubleshooting: http://getchatty.sourceforge.net/help-issues.html

Java Crash: Graphics Driver

Solution/Workaround
Seems to have to do with the graphics driver. Reinstalling/updating the graphics driver may help or using the following commandline parameter, which should disable the calls to the library that cause the error:

-Dsun.awt.nopixfmt=true

詳しくはわかりませんが、ここら辺見てみるとddraw.dllをロードしないようにするパラメータらしいです。

<installation folder>\etc\netbeans.conf の netbeans_default_options 
-J-Dsun.awt.nopixfmt=true」 を追加してみました。
すると、しばらく使ってみてもクラッシュしなくなったようです!
とりあえずこのパラメータで回避できるみたいです。