VC++11製バイナリをXPで動かす

2012年4月1日現在、VisualC++11 Betaで作成したバイナリはXPで動作しません。
今回は、それを強引にXPで動くようにしてしまう方法を紹介します。

なぜXPで動かないのか?

理由は簡単で

  • 動作対象OSがVista以上
  • ランタイムでVista以降のAPIを使用

だから。

動作対象OSをXPに変更

exeファイルには動作対象のOSを指定するサブシステムバージョンというものが存在し、それに満たない環境では動作しないようになっています。
本来ならリンカオプションのsubsystemで変更できるのですが、VC++11からはXP(5.1)以下を選択できません。
なので、強引にexe冒頭で指定されているサブシステムバージョンを5.1に書き換えることで回避します。

具体的には
ここを05 00 01 00と書き換えるとXP以上という意味になります。

書き換えている場所について詳しく知りたい人は、IMAGE_NT_HEADERSのOptionalHeader.MajorSubsystemVersionとOptionalHeader.MinorSubsystemVersionでググってください。

Windowsのバージョン番号はwikipediaの「Windows のタイムライン」を参照

Vista以降のAPI呼び出しを止める

動作対象のOSをXP以上にしたとしても、XPに存在しないAPIを呼んでいては起動すらできません。
これを回避する方法はいくつかあるのですが、ここではダミーのKernel32.dllを作成し、そこに必要なAPIを独自実装することで回避することにします。

exeファイルには当然リンクするべきdll名が記述されています。
そのうちKERNEL32.dllを書き換え、ダミーのDLL名(ここではKernelXP.dllとします)に変更し、そのDLL越しにKERNEL32.dllを呼ぶように細工します。
これは単にexeファイル内からKERNEL32.dllという文字列を探し、書き換えるだけで可能です。

つぎにダミーのDLLの作り方ですが、まず完成品がこちら。
KernelXP.zip

作り方としては、XPのKERNEL32.dllでエクスポートされている関数の一覧を作り
※エクスポート関数一覧の取得方法はDllFuncList.zipなどを参照
それらをすべてKERNEL32.dllへ受け流すだけのDLLを作成。
その後exeファイルをXPで実行してみて、APIが見つからないエラーが出るたびに、そのダミー実装を作るを繰り返すだけ。
詳しくは、DLLインジェクションやダミーDLLなどでググると見つかるかと思います。

終わりに

今回はダミーのDLLを経由させる方法で実現しましたが、一般的にはIATを書き換える方が多いようです。
いつかそちらについても言及してみたいなと思いつつ、今回はここまで。

th123_aiVer0.95リリース

変更点の大部分をすでに覚えていないのだが、何とか書き出してみる。

Ver0.95

  • get_obj_dataで返るHPがおかしかった不具合の修正
  • 環境変数act_blockの追加
  • コンパイル環境の変更
  • get_special_dataの13と14が動作するように
  • PANIC後の再読み込みが機能していなかったのを修正
  • 非想天則1.10aに対応

get_obj_dataはたぶん何か計算ミスってたんだと思います。

act_blockは従来までの*_frame系がリセットされて判別できない問題に対応するため
frameがリセットされるたびにインクリメントされている値を入れました。
ちなみに、自力計算ではなくth123.exeの中から取得しています。

コンパイル環境は何度か変えました。
最新はVC++11Beta Ultimate。
VC++11ではXP向けバイナリは作れないんですが、無理やり力技で何とかしてます。

get_special_dataはどうやったかは覚えてないけど動くようになったらしいです、たぶん。

PANIC後の再読み込みがなぜか動いてなかったので修正。
なぜかファイル名のリセットがかかっていて、再読み込みしようとして失敗して落ちるというアホかましてました。

1.10a対応はちゃんとアドレスサーチして入れました。
が、結果は有志の方が作ってくれたiniとほぼ一致していたので無駄でしたね。

get_hitareaにバグあるらしいので、近いうちに直した版をリリース予定。
一週間以内には出るんじゃないかな。
たぶん、たぶんね。

サーバーメンテナンスがあるらしい

ここやAIManager用のサーバーが置いてあるxreaのサーバーですが
近々全物理マシーンを刷新するそうなので、たぶん6時間程度つながらなくなるようです。

スケジュールはまだ未定ですが、つながらなくても一時的なものなので気にしないでください。

Google Code Jam Japan 2011 予選

Google Code Jam Japan 2011 予選に出場。
スコアは全問正解、誤答なし、ペナルティ5:31:41、918人中の145位でした。
ちなみに参加が遅かったわけではなく、ほぼ最初から参加してこのタイムです、コーディング遅いなぁ。

問題A. カードシャッフル

概要:1~Mまで順に並んだカードの山があり、上から何枚目から何枚目までを1番上に移動させる操作をなんどか行った後、上から指定枚数眼のカードの数字は何か、という問題。
提出したソースコード:

001 
002 #include <stdio.h>
003 #include <stdlib.h>
004 #include <math.h>
005 #include <limits.h>
006 
007 #include <iostream>
008 #include <fstream>
009 #include <string>
010 #include <vector>
011 #include <list>
012 #include <map>
013 #include <set>
014 #include <iterator>
015 #include <numeric>
016 #include <functional>
017 #include <algorithm>
018 #include <utility>
019 
020 // using boost C++ library Version 1.47.0>=
021 // http://www.boost.org/
022 #include <boost/assert.hpp>
023 #include <boost/foreach.hpp>
024 
025 typedef std::pair<unsigned int, unsigned int> cut;
026 
027 std::list<cut>::iterator CutFirst(std::list<cut> &card_list, unsigned int pos) {
028     unsigned int cur_pos = 1;
029     std::list<cut>::iterator it = card_list.begin();
030     while(it != card_list.end()) {
031         const unsigned int cur_size = 1 + it->second - it->first;
032         if(cur_pos <= pos && pos < cur_pos + cur_size) {
033             break;
034         }
035         cur_pos += cur_size;
036         ++it;
037     }
038     BOOST_ASSERT(it != card_list.end());
039     if(cur_pos != pos) {
040         it = card_list.insert(it, std::make_pair(it->first, it->first + (pos - cur_pos - 1)));
041         ++it;
042         it->first = it->first + (pos - cur_pos);
043     }
044     return it;
045 }
046 
047 void CutEnd(std::list<cut> &card_list, std::list<cut>::iterator start_it, unsigned int size) {
048     std::list<cut>::iterator it = start_it;
049     std::list<cut> insert_cut;
050     unsigned int cut_size = size;
051     while(cut_size > it->second - it->first + 1) {
052         cut_size -= it->second - it->first + 1;
053         insert_cut.push_back(*it);
054         it = card_list.erase(it);
055         BOOST_ASSERT(it != card_list.end());
056     }
057     const unsigned int cur_cut_size = it->second - it->first + 1;
058     if(cut_size == cur_cut_size) {
059         insert_cut.push_back(*it);
060         card_list.erase(it);
061     } else if(cut_size < cur_cut_size) {
062         insert_cut.push_back(cut(it->first, it->first + cut_size - 1));
063         it->first = it->first + cut_size;
064     } else {
065         BOOST_ASSERT(false);
066     }
067     card_list.insert(card_list.begin(), insert_cut.begin(), insert_cut.end());
068 }
069 
070 unsigned int GetCardNumber(unsigned int card_max, unsigned int get_index, const std::vector<cut> &cut_list) {
071     std::list<cut> card_list;
072     card_list.push_back(std::make_pair(1, card_max));
073     BOOST_FOREACH(const cut &cut_pair, cut_list) {
074         const std::list<cut>::iterator it = CutFirst(card_list, cut_pair.first);
075         CutEnd(card_list, it, cut_pair.second);
076     }
077     const std::list<cut>::iterator it = CutFirst(card_list, get_index);
078     return it->first;
079 }
080 
081 int main(unsigned int argc, const char * const *argv) {
082     if(argc != 2) {
083         return 0;
084     }
085     std::ifstream ifs(argv[1]);
086     if(!ifs.is_open()) {
087         return 0;
088     }
089     unsigned int t;
090     ifs >> t;
091     for(unsigned int i = 0; i < t; i++) {
092         unsigned int m, c, w;
093         ifs >> m >> c >> w;
094         std::vector<cut> cut_list(c);
095         BOOST_FOREACH(cut &ab, cut_list) {
096             ifs >> ab.first >> ab.second;
097         }
098         std::cout << "Case #" << (i+1) << ": " << GetCardNumber(m, w, cut_list) << std::endl;
099     }
100     return 0;
101 }

カードが最大で10^9枚になるので、配列ではメモリーが足りません。
しかしカット回数は最大でも100回なので、連番が維持されている物は最初と最後の番号だけ残して省略することで表現が可能です。
後は特に難しい所はないので、そのまま解くだけです。

問題B. 最高のコーヒー

概要:毎日珈琲を飲む人がいます。 各種珈琲について満足度/残量/賞味期限が与えられたとき、最大の満足度はいくつか。
提出したソースコード:

001 
002 #include <stdio.h>
003 #include <stdlib.h>
004 #include <math.h>
005 #include <limits.h>
006 
007 #include <iostream>
008 #include <fstream>
009 #include <string>
010 #include <vector>
011 #include <list>
012 #include <map>
013 #include <set>
014 #include <iterator>
015 #include <numeric>
016 #include <functional>
017 #include <algorithm>
018 #include <utility>
019 
020 // using boost C++ library Version 1.47.0>=
021 // http://www.boost.org/
022 #include <boost/assert.hpp>
023 #include <boost/foreach.hpp>
024 #include <boost/tuple/tuple.hpp>
025 
026 #define EMPTY_COFFEE    UINT_MAX
027 
028 struct coffee_tuple {
029     unsigned long long int count;
030     unsigned long long int last_time;
031     unsigned int manzoku;
032 };
033 struct nissuu_tuple {
034     nissuu_tuple(unsigned long long int start, unsigned long long int size, unsigned int coffee_id) : start(start), size(size), coffee_id(coffee_id) { }
035     unsigned long long int start;
036     unsigned long long int size;
037     unsigned int coffee_id;
038 };
039 
040 std::list<nissuu_tuple>::iterator GetEmptyPos(std::list<nissuu_tuple> &nissuu_list, unsigned long long int last, unsigned long long int max) {
041     std::list<nissuu_tuple>::iterator last_empty_it = nissuu_list.end();
042     for(std::list<nissuu_tuple>::iterator it = nissuu_list.begin(); it != nissuu_list.end(); ++it) {
043         if(it->start <= last && last < it->start + it->size) {
044             if(it->coffee_id == EMPTY_COFFEE) {
045                 if(it->start + it->size > last + 1) {
046                     const unsigned long long int start = it->start;
047                     const unsigned long long int insert_size = last - start + 1;
048                     it->start = start + insert_size;
049                     it->size -= insert_size;
050                     it = nissuu_list.insert(it, nissuu_tuple(start, insert_size, EMPTY_COFFEE));
051                 }
052                 last_empty_it = it;
053             }
054             break;
055         }
056         if(it->coffee_id == EMPTY_COFFEE) {
057             last_empty_it = it;
058         }
059     }
060     if(last_empty_it == nissuu_list.end()) {
061         return nissuu_list.end();
062     }
063     std::list<nissuu_tuple>::iterator ret = last_empty_it;
064     if(max < ret->size) {
065         const unsigned long long int insert_size = ret->size - max;
066         ret = nissuu_list.insert(ret, nissuu_tuple(ret->start, insert_size, EMPTY_COFFEE));
067         ++ret;
068         ret->start += insert_size;
069         ret->size = max;
070     }
071     return ret;
072 }
073 
074 void Drink(std::list<nissuu_tuple> &nissuu_list, coffee_tuple coffee, unsigned int coffee_id) {
075     while(coffee.count > 0) {
076         std::list<nissuu_tuple>::iterator it = GetEmptyPos(nissuu_list, coffee.last_time, coffee.count);
077         if(it == nissuu_list.end()) {
078             break;
079         }
080         it->coffee_id = coffee_id;
081         coffee.count -= it->size;
082     }
083 }
084 
085 struct SortManzoku {
086     bool operator()(const coffee_tuple &left, const coffee_tuple &right) const {
087         return left.manzoku > right.manzoku;
088     }
089 };
090 
091 unsigned long long int GetManzoku(unsigned long long int nissuu, std::vector<coffee_tuple> &coffee_list) {
092     std::sort(coffee_list.begin(), coffee_list.end(), SortManzoku());
093     std::list<nissuu_tuple> nissuu_list;
094     nissuu_list.push_back(nissuu_tuple(1, nissuu, EMPTY_COFFEE));
095     unsigned int id = 0;
096     BOOST_FOREACH(coffee_tuple &coffee, coffee_list) {
097         Drink(nissuu_list, coffee, id);
098         id++;
099     }
100     unsigned long long int ret = 0;
101     BOOST_FOREACH(nissuu_tuple &nissuu, nissuu_list) {
102         if(nissuu.coffee_id == EMPTY_COFFEE) {
103             continue;
104         }
105         ret += nissuu.size * coffee_list[nissuu.coffee_id].manzoku;
106     }
107     return ret;
108 }
109 
110 int main(unsigned int argc, const char * const *argv) {
111     if(argc != 2) {
112         return 0;
113     }
114     std::ifstream ifs(argv[1]);
115     if(!ifs.is_open()) {
116         return 0;
117     }
118     unsigned int t;
119     ifs >> t;
120     for(unsigned int i = 0; i < t; i++) {
121         unsigned int n;
122         unsigned long long int k;
123         ifs >> n >> k;
124         std::vector<coffee_tuple> coffee_list(n);
125         BOOST_FOREACH(coffee_tuple &coffee, coffee_list) {
126             unsigned long long int c, t;
127             unsigned int s;
128             ifs >> c >> t >> s;
129             coffee.count = c;
130             coffee.last_time = t;
131             coffee.manzoku = s;
132         }
133         std::cout << "Case #" << (i+1) << ": " << GetManzoku(k, coffee_list) << std::endl;
134     }
135     return 0;
136 }

intでは収まりきらない数値を扱うのでオーバーフローに注意が必要。
Aと同じく日数が2*10^12なので、配列ではなく期間の始点と長さの形で表現しています。
問題自体は満足度の高い珈琲から順に出来る限り賞味期限ぎりぎりに飲むようにするだけで解くことができます。

問題C. ビット数

概要:数値Nが渡されたとき、a+b=Nが成り立つaとbにおいて、二進数表記した時に1が現れる個数が最大の組み合わせを探し、その1の個数を返せ。
提出したソースコード:

001 
002 #include <stdio.h>
003 #include <stdlib.h>
004 #include <math.h>
005 #include <limits.h>
006 
007 #include <iostream>
008 #include <fstream>
009 #include <string>
010 #include <vector>
011 #include <list>
012 #include <map>
013 #include <set>
014 #include <iterator>
015 #include <numeric>
016 #include <functional>
017 #include <algorithm>
018 #include <utility>
019 
020 // using boost C++ library Version 1.47.0>=
021 // http://www.boost.org/
022 #include <boost/assert.hpp>
023 #include <boost/foreach.hpp>
024 
025 unsigned int GetMaxBitNumber(const std::vector<unsigned long long int> &ans_list, unsigned long long int n) {
026     for(unsigned int i = 0; i < ans_list.size(); i++) {
027         if(ans_list[i] > n) {
028             BOOST_ASSERT(i > 0);
029             return i - 1;
030         }
031     }
032     return ans_list.size() - 1;
033 }
034 unsigned int GetBit(unsigned long long int n) {
035     unsigned int ret = 0;
036     for(; n > 0; n /= 2) {
037         if(n%2 == 1) {
038             ret++;
039         }
040     }
041     return ret;
042 }
043 unsigned int GetMinBitNumber(const std::vector<unsigned long long int> &bit, unsigned long long int n) {
044     for(unsigned int i = 0; i < bit.size(); i++) {
045         if(bit[i] == n) {
046             return i;
047         }
048         if(bit[i] > n) {
049             BOOST_ASSERT(i > 0);
050             return i - 1;
051         }
052     }
053     return bit.size() - 1;
054 }
055 unsigned int GetBitNumber(const std::vector<unsigned long long int> &bit, const std::vector<unsigned long long int> &ans_list, unsigned long long int n) {
056     const unsigned int max = GetMaxBitNumber(ans_list, n);
057     if(ans_list[max] == n) {
058         return max;
059     }
060     const unsigned int min = GetMinBitNumber(bit, n);
061     unsigned int ret = 0;
062     for(unsigned int i = 0; i <= min; i++) {
063         ret = std::max(ret, i + GetBit(n - bit[i]));
064     }
065     return ret;
066 }
067 
068 int main(unsigned int argc, const char * const *argv) {
069     std::vector<unsigned long long int> bit;
070     for(unsigned int i = 0; i < 64; i++) {
071         if(i == 0) {
072             bit.push_back(0);
073             continue;
074         }
075         bit.push_back(bit.back()*2+1);
076     }
077     std::vector<unsigned long long int> ans_list;
078     for(unsigned int i = 0; i <= 124; i++) {
079         if(i == 0) {
080             ans_list.push_back(0);
081             continue;
082         }
083         const unsigned int index = i / 2;
084         if(i%2 == 1) {
085             ans_list.push_back(bit[index] + bit[index + 1]);
086         } else {
087             ans_list.push_back(bit[index] * 2);
088         }
089     }
090 
091     if(argc != 2) {
092         return 0;
093     }
094     std::ifstream ifs(argv[1]);
095     if(!ifs.is_open()) {
096         return 0;
097     }
098     unsigned int t;
099     ifs >> t;
100     for(unsigned int i = 0; i < t; i++) {
101         unsigned long long int n;
102         ifs >> n;
103         std::cout << "Case #" << (i+1) << ": " << GetBitNumber(bit, ans_list, n) << std::endl;
104     }
105     return 0;
106 }

Nが最大で10^18になるのでオーバーフローに注意が必要。
なぜかNの下限が制約に記されていませんが、問題文中に正の整数とあるので0<=N<=10^18と解釈していいと思われる。 aかbのうち片方を全て1が並ぶ数値にし、もう片方を余りとして計算する総当たりで解いている。 中途半端に枝刈りをしているが、計算量から考えて明らかに無駄、総当たりでも何ら問題はない。 (どんなに大きくても64回計算すれば答えが出る計算、一回当たりの計算量もたかが知れているため、最適化するだけ無駄) どれも問題を読んで大まかな流れを決めるまではすぐだったが、実際組んでみたらボロが出るの連続。 たとえばデータ構造にしても、あとからメンバーを足したり減らしたり、まるで違うクラスにしたりといったミスが目立った。 正直、この内容で5時間以上もかかったのは自分でも信じられないほど。 自分には解法を導き出す力が足りないと思っていたが、今回の結果をみるとコードを書く力自体も足りないようだ。 来週の本選ではその点にも気を付けて参加してみようと思う。

xrea広告でwordpressに不具合が発生する理由

無料版xreaサーバーにwordpressを設置すると管理画面において様々な不具合に遭遇します。
ですが、その件について調べても回避方法ばかりで、原因については”広告が原因らしい”以上の事がわかりませんでした。

“動けばそれでいい”は主義に反するので、きちんと調べてみましょう。
直し方を知りたいだけの方は他のサイト様をご覧ください。
なお、以降の記述はwordpress3.2.1-jaをPHP5.2.5がインストールされているXREA無料サーバーの自動広告挿入をONの上で動かした場合の物となります。


問題を正確に把握する

まずは起きている問題を正確に把握しましょう。

管理ページはどこも以下のように表示が乱れています。

どうやらスタイルシートがおかしいようなので、片っ端からソースを覗いてみます。
するとwp-admin/load-styles.php(パラメーターは省略)の中身が

このようにバイナリファイルをテキストエディタで開いたかのようになっています。
また、Content-Typeもtext/htmlとなっており(text/cssでなければいけない)、明らかに動作がおかしいことがわかります。

さらに、管理画面の外観->ウィジェット(このブログでいう右側のメニューなどを編集する機能です)では、本来D&D出来るはずの項目が全く反応しません。
今度はjavascriptが怪しいのでソースから探してみると、wp-admin/load-scripts.php(パラメーターは省略)がCSSの時と同様に化けていました。
Content-Typeも同様にtext/htmlになっていました(application/x-javascript; charset=UTF-8が正しい)。
※javascriptは色々難しい事情があり、上記のapplication/x-javascriptは見方によっては正しくないこともありますが、今回は関係ないので割愛。

ざっと調べた範囲ですが、どうやらこの二点が主な問題点のようです。

本当に原因は広告なのか

.htaccessにLayoutIgnoreURI *と記述して自動広告挿入をOFFにし、本当に治るのか確かめてみます。
すると、確かに文字化けせずContent-Typeも正常化したことがわかります。

しかし、自動広告挿入がONであってもCSSやjavascriptは広告挿入対象ではありません。
一体何が起こっているのでしょう?

xreaの広告挿入処理をハック

ハックはハックでも「調べる、解析する」の方で、クラッキング(破壊する、損害を負わせる)という意味ではありません、念のため。

ためしに「//test」の一行をjavascriptとして返すphpを作って広告の挙動を試してみます。
※実際に試したコード


<php
header('Content-Type: application/x-javascript; charset=UTF-8');
echo "//test";
exit;

すると、1行目に謎の空行が挿入されていました。
また、Content-Typeもtext/htmlになっています。
もちろん自動広告挿入がOFFだと正常動作、ますます謎です。

なぜこのようなことが起きるのでしょうか?
どうやらheader命令よりも前に空行が出力されているのが原因のようでした。
実際phpのマニュアル(http://php.net/manual/ja/function.header.php)にて

header() 関数は、 通常の HTML タグまたは PHP からの出力にかかわらず、すべての実際の 出力の前にコールする必要があることです。

と説明されています。

おそらく、自動広告挿入がONであれば必ず1行は挿入される処理なのでしょう。
そしてPHPでは(自分が知っている範囲では)Content-Typeを変更する方法はheader関数以外にないので、それが原因でPHPのデフォルトであるtext/htmlになってしまうこともわかりました。

ですが、これだけだと本来テキストであるはずの出力が化けている理由がわかりません。
今度はまた別方面から調べてみましょう。

テキストが化ける理由を探す

まずheader関数が失敗するらしいことはわかったので、広告自動挿入ONとOFFで来ているhttpヘッダーを比べてみます。
すると、自動広告挿入がOFFの時だけ「Content-Encoding: deflate」が返っていることがわかります。

ためしにload-styles.phpとload-scripts.phpを覗いてみると、最後の方にある


		header('Content-Encoding: deflate');
		$out = gzdeflate( $out, 3 );

というコードでテキストを圧縮していることがわかりました。

どうやらブラウザが対応している場合は圧縮して返すが、header関数が失敗してしまうため、ブラウザはテキストだと勘違いしてそのまま使うために化けているようです。
実際、この圧縮処理を丸々コメントアウトしてみたところ、広告挿入処理がONであってもきちんとテキストは出力されるようになりました。
(※正確には、PHPはContent-Typeによってechoやprintの処理を分岐しているため、ブラウザに送信する以前から破損データとなっています)

まとめ

・自動広告挿入がONだと、たとえどんな内容でも空行が挿入される(xrea広告自動挿入機能の弊害)
・たとえ空行だろうと1行でも出力されるとheader関数が失敗する(PHPの動作仕様)
・header関数でContent-TypeとContent-Encodingを設定しているため、これらが失敗することで不具合が発生している

ちなみにload-*.php側の修正だけで完全に回避することはできません
(どうやってもContent-Typeを設定する方法がないため)
ここまで延々調べましたが、結局LayoutIgnoreURIで広告を解除するのが一番妥当な回避策となります。

要するにPHP使うならサーバー代金ぐらい払おう、というお話でした。

特訓の成果

継続して3km程度のランニング可。
腕を胸に組んでの腹筋運動最高連続22回達成。
腕立て伏せはいまだやり方すらわからない。

後わかったこととして、筋肉痛とはまた別の継続疲労みたいなものが存在する。
今現在、ランニング3km一回では筋肉痛は出なくなった、けれど足にダルさのようなものが1~2日程度出て、揉むと気持ちいい感じになる。
今思い返してみると、筋肉痛が酷いから動けないと思っていた日も、動かない理由の大半はこの倦怠感が原因だった気がする。

ので、会社復帰した後は運動するにしても3km走るのはまずい。
引っ越しなどで忙しくはあるが、復帰後も体力作りに励むつもりなので、この期間中に継続疲労が出ないギリギリの運動量を調べないとね。

腹筋運動って

腕使っちゃいけないんですね。
今まで腕を思いっきりぶんぶんしていた自分、道理で回数多いはずだよ。

ちなみに、腕なしだと9回でした。
これでも肉体労働してる母親より多い、まだ何か間違ってんのかなぁ

リファラの有無で動作を変えるサイト

は本当にやめてほしい。

そもそもリファラはrfcにおいてオプションと定義されており、送信は必須ではない。
むしろ「セキュリティー的な観点からユーザーが送信するかどうかを選択できる形が望ましい」(意訳)とすら記載されている。
(詳しくは ttp://www.ietf.org/rfc/rfc2616.txt を参照の事)

にもかかわらず、リファラを送信しなければ利用できないサービスが大量に存在しており、そのサービスの利用をやめるか、毎回リファラの送信/未送信を切り替えて利用しなければいけなくなっている。
これは非常によろしくない、利用者の側も、運営側もだ。

まず第一に、なぜリファラを送信しないと動作しないサービスが存在するのか?
最も一般的な理由は、XSS対策や”外部サイトから意図しない形で自サービスを利用されない為”だろう。
だがこれは、リファラを利用しても完璧に防ぐことはできないし、他にも対策のしようはいくらでもある場合が多い。
そもそも”リファラは送らなくてもよい”ものであるから、こういう用途で用いることは不適当である。

また、中にはリファラーによって情報を受け渡すことで動作しているサービスも存在している。
こちらは完全に論外であり、リファラに頼らない設計を行うべきである。
中にはリファラ内にユーザーIDを含め、それを元に動作しているものがあるが、そんなサイトから外部のサイトへリンクが張られていた日には、リファラからユーザーIDが漏えいし好き勝手されてしまうことだろう。

以上のように、リファラでなければならないものはなく、Webサイト側に不備があればリファラから個人情報が漏えいすることすらあるのだ。
個人情報保護のため、リファラを未送信のまま利用できることは我々ユーザーの権利であるとすら言える。

なので、リファラがないと動作しないサイトはリファラ未送信でも動くようになってください、ほんと不便なので……

お医者さん曰く

筋肉痛は普通一週間も続かないらしい。
※専門の方ではなく、精神科の方です

「普通は長くても二日だ、嘘言うんじゃないよ」(意訳)と言われてほんとどうしようかと思った。
実は4月引きこもってた直後は2週間続いた、なんてことは到底言い出せる雰囲気ではなく、
何故か言い訳のように、今どれだけ筋肉落ちているか説明するという意味わからない展開に。

なんで精神科いって逆に不安にならなきゃならんのか、だいぶ良くなったとはいえ自分鬱病なんですけど!
しかも、心配してるとか、病気の疑いがあるとか、そんなではなく人の言うこと信じない”だけ”というのはちょっと……
今は「やっぱり医者は信用ならねー」と愚痴るだけで終われるけど、酷い時だったら通院拒否ってたぞこんちくしょー

ちなみに、筋肉痛について軽くググった範囲でも一週間程度までなら普通に話が出てくる。
ただし特に心当たりがないのに一週間続く場合は別原因が疑われるので注意とかなんとか。
(自分の場合、心当たりがあり、長いとはいえ二週間ほどで収まったため単なる筋肉痛だと思っておく)
ちなみに、お医者さんからこの辺の話は一切出ませんでした。

やっぱりお医者さんは信頼したら負けですね。
鬱病だって事もこっちから聞くまで言わなかったし(自律神経失調症だと言われていたが、鬱的と言ったり鬱の薬が出てるので質問してようやく)
薬の副作用に合わない処方だったり(後で薬剤師さんに聞いたら薬剤師さんは知っていた)
完治判断の目安も聞かなきゃ答えてくれないし(治ったように見えて日常生活に戻るとすぐ崩すのはよくあるらしく、2月良好が一種の目安だとか)
直すための行動も聞かなきゃ何も言われないし、全部こっちで調べて提案して承諾だけ求めるのがちょうどいいレベル。
※こっちが何も言わないと、現在の体調を聞き、場合により薬を調整し、次回の予約をするだけである。

とりあえず「医者に行って言われた通りにするだけじゃ病気は治らない」これだけはガチ。
それで治せるのは名医と言われてる極一部だけなので、病気の時はお医者さんを問い詰めるつもりでいきましょう。