ホーム > tohokuaiki

tohokuaiki

データのどれだけの個数を調べれば、だいたい「まぁ、いいかな」って言えるか。

食品総合研究所 :食品のサンプリングに関するガイダンス〜品質情報解析ユニット から。

10000個、製品があってこの中の不良品を見つけられる確率の話。1個でも不良品があってその存在が致命的な場合は全品検査になる。 全品検査が大変な時は、サンプルN個だけを抜き出して検査をしたいが、どれだけのNを検査すればいいのかの妥当性を知りたい。

ポイントは、見逃し率。これをできるだけ下げたいが、Nが多すぎても面倒なのでその経済効率の最大リターンポイントを見つける。

ただし、1個の検査をするときにその検査による判定は100%の確からしさで判定できるものとする。

用語の設定

  • 不良品率:全体の何%が不良品かの確率。
  • 見逃し率:サンプルN個検査をした際に、どれくらいの確率でそれが「不良品が存在した」と発見できるかの確率。

計算方法

1個の製品をチェックする際に、それが不良品でありかつ不良品であると判定できる確率を計算する。パーセンテージで考えると100掛けたり割ったり面倒なので、確率は少数でやる。

合格品発見確率 = 1 - 不良品率
N個全てが合格品である確率 = (1 - 不良品率)^N (^はNの乗数を意味する)
N個のうち1個でも不良品が見つかる確率 = 1 - (1 - 不良品率)^N 

この「N個のうち1個でも不良品が見つかる確率」というのは、「不良品を発見できる確率」とも言えます。つまり、「1-見逃し率」

1 - 見逃し率 =  1 - (1 - 不良品率)^N 

となる。

Nについて解くと、

N = log(見逃し率) ÷ log(1 - 不良品率)

となる。

はてなスターAPIを使ってはてなブックマークコメントについた★の数を得る

はてなスター取得APIというのがある。

はてなスター取得 API - Hatena Developer Center

これで、はてなブックマークのコメントについた★の数を得ようとする。

例えば、https://anond.hatelabo.jp/20180518171957 に付けた私のブックマークの★の数はこの記事を執筆時点で91個。

このブックマークのURLは、http://b.hatena.ne.jp/entry/364742428/comment/tohokuaiki でカノニカルだと思われる。

なので、先ほどのAPIに従って、
http://b.hatena.ne.jp/entry/364742428/comment/tohokuaiki
をくっつけて
http://s.hatena.com/entry.json?uri=http%3A%2F%2Fb.hatena.ne.jp%2Fentry%2F364742428%2Fcomment%2Ftohokuaiki
で取得できるのかな?と思ったらだめだった。

正解は
http://b.hatena.ne.jp/tohokuaiki/20180525#bookmark-364742428
をくっつけて
http://s.hatena.com/entry.json?uri=http%3A%2F%2Fb.hatena.ne.jp%2Ftohokuaiki%2F20180525%23bookmark-364742428
なのである。なんだよ、このアンカー入りのURLでパーマリンク設定するのって…。

WordPressで特定の投稿タイプで特定の部分だけ自動整形のPタグを消す (wpautopを動作させない)

filterの削除・追加とショートコードを使う

custom_posttype投稿タイプの場合。

<?php
    /* 特定の投稿タイプはautopをしない */
    remove_filter('the_content','wpautop');
    add_filter('the_content' , function($content){
        $post_type = get_post_type();
        if ($post_type != 'custom_posttype'){
            $content = wpautop($content);
        }
        return $content;
    });
    add_shortcode('wpautop', function($attr, $content){
        $post_type = get_post_type();
        if ($post_type == 'custom_posttype'){
            $content = wpautop($content);
        }
        return $content;
    });

で、本文では、

この部分はautopが効く。

[wpautop]
この部分は、そのまま出る。
この部分は、そのまま出る。
[/wpautop]

という感じ。

ApacheのProxyをかます途中でBasic認証を入れてConfluenceにアクセスさせようとしたら失敗した件

よくある1サーバーでConfluenceを複数稼働させたい場合のリバースProxy設定ですね。

こんな感じ。

あるいは、iptablesで無駄にポートを開けたくない場合とか。

で、内輪向けのConfluenceなんで、Basic認証をかければゼロデイアタックとかも多少は防げるんじゃないかって思ってBasic認証つけたかったんです。

Basic認証は通るけど、ConfluenceでAuthエラー

余裕じゃん・・と思ってこんな感じでProxy設定にBasic認証の設定をかけたわけです。

<VirtualHost *:80>
    <Location />
        Order deny,allow
        Allow from all
        AuthType Basic
        AuthName "Authentication"
        AuthUserFile /etc/apache2/htpasswd
        Require valid-user
    </Location>
    ProxyTimeout 8000
    ProxyRequests Off
    ProxyPreserveHost On
    SetEnv force-proxy-request-1.0 1
    SetEnv proxy-nokeepalive 1
    ProxyPass / http://localhost:8090/ retry=1 acquire=3000 timeout=600 Keepalive=On
    ProxyPassReverse / http://localhost:8090/
</VirtualHost>

すると、こんなエラーが出る。

HTTPステータス 401 - Basic Authentication Failure - Reason : AUTHENTICATION_DENIED
type ステータスレポート
メッセージ Basic Authentication Failure - Reason : AUTHENTICATION_DENIED
説明 This request requires HTTP authentication.
Apache Tomcat/8.0.50

なんでTomcat側でエラーが出るんだ????

と思い、Tomcat使ってるのはConfluenceなのでこちら側を疑う。Confluenceで一旦ログイン後に、ApacheBasic認証をつけるとこのエラーは出ない。

ということは、Confluenceは

  1. COOKIEによる認証を試みる
  2. COOKIE認証に失敗した場合、Basic認証のAuthorization ヘッダから認証を試みる

となっているのではないか???

余計なことしてほしくないなぁ…と思い、そのAuthenticateをしているだろうプラグインを探す。

なんとなくこいつっぽいなーって思い、「無効」にしたが全然だめだった。

Confファイルを探すもなんか見つからない…。

ということで、Apache側で設定

だったら、Proxyする際にAuthenticationヘッダを送らなければいいんじゃんって思って、Apacheにmod_headersを導入

# a2enmod headers

した後に、apache.conf(debianなんでsites-enables内だけど)を書き換えて

    RequestHeader unset Authorization

を追加。

すると問題なくConfluence側がBasic認証をやめてくれた。

Confluence、REST and os_authTypeとか読むとquery stringsにos_authType=basicって入れないとBasic認証ヘッダをスルーしてくれそうなんだけどな。ま、いいや。。

PHPで制御文字を取り除く

ほかのプログラミング言語はたくさんあったのだけどPHPが無い。

Strip control codes and extended characters from a string - Rosetta Code

こんな感じかな。

<?php
function stripCtrlCode($string)
{
    $ctrl_code = array(
        "00", // NULl(ヌル)
        "01", // Start Of Heading(ヘッダ開始)
        "02", // Start of TeXt(テキスト開始)
        "03", // End of TeXt(テキスト終了)
        "04", // End Of Transmission(転送終了)
        "05", // ENQuiry(問合せ)
        "06", // ACKnowledge(肯定応答)
        "07", // BELl(ベル)
        "08", // Back Space(後退)
        /*    "09", // Horizontal Tabulation(水平タブ) */
        /*    "0A", // Line Feed(改行)*/
        "0B", // Vertical Tabulation(垂直タブ)
        "0C", // Form Feed(改ページ)
        /*    "0D", // Carriage Return(復帰)*/
        "0E", // Shift Out(シフトアウト)
        "0F", // Shift In(シフトイン)
        "10", // Data Link Escape(伝送制御拡張)
        "11", // Device Control 1(装置制御1)
        "12", // Device Control 2(装置制御2)
        "13", // Device Control 3(装置制御3)
        "14", // Device Control 4(装置制御4)
        "15", // Negative AcKnowledge(否定応答)
        "16", // SYNchronous idle(同期信号)
        "17", // End of Transmission Block(転送ブロック終了)
        "18", // CANcel(取消)
        "19", // End of Medium(媒体終端)
        "1A", // SUBstitute(置換)
        "1B", // ESCape(拡張)
        "1C", // File Separator(ファイル分離)
        "1D", // Group Separator(グループ分離)
        "1E", // Record Separator(レコード分離)
        "1F", // Unit Separator(ユニット分離)
        );

    $code_array = array();
    foreach ($ctrl_code as $code){
        $code_array[] = hex2bin($code);
    }

    return str_replace($code_array, "", $string);
}

って作った後に軽くググったら

制御文字を取り除く方法(改行コードは保持) - Qiita

<?php
return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $string); 

ってあって、なんだか ( ´_ゝ`)って表情になった。

polyglotで英語の構文解析をする

日本語だと、MeCabとか入れておけば大体大丈夫で日本語での検索も捗るんだけど、英語って言われると…となってしまったので

lab.astamuse.co.jp

を参考にCentOS7で解析してみる。

インストール

とりあえず環境

$ more /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

実行

python-numpyが必要らしい。・・・んだけど、yum listしてみると、python27-numpyしかない。

しかし、んー、27ってついてるけど・・・・

[itoh@localhost ~]$ which python2.7
/bin/python2.7
[itoh@localhost ~]$ which python
/bin/python
[itoh@localhost ~]$ ls -l /bin/python
lrwxrwxrwx 1 root root 7 Jan 26 11:05 /bin/python -> python2
[itoh@localhost ~]$ ls -l /bin/python2
lrwxrwxrwx 1 root root 9 Jan 26 11:05 /bin/python2 -> python2.7

まぁ、大丈夫そうだろう。

pipもpip2.7がいいのかな~?と思ってインストールしたけど diff /bin/pip /bin/pip2.7 したら違いがなかった。

# yum install python27-numpy
# yum install -y libicu-devel
# yum install -y pip2.7
# yum install python-devel
# pip2.7 install --upgrade pip
# pip2.7 install polyglot

Installing collected packages: PyICU, pycld2, morfessor, numpy, wheel, polyglot
  Running setup.py install for PyICU ... done
  Running setup.py install for pycld2 ... done
  Running setup.py install for morfessor ... done
  Running setup.py install for polyglot ... done
Successfully installed PyICU-2.0.3 morfessor-2.0.4 numpy-1.14.2 polyglot-16.7.4 pycld2-0.31 wheel-0.30.0

となったので問題ないんだろう。

モデルファイルのインストール

$ polyglot download embeddings2.en pos2.en

で、自分のホームディレクトリにpolyglot_dataディレクトリを作成してこれがモデルファイルらしい。

で、何をしたかったかというと、

PDFから抜き出したテキストファイルを字句解析したかったのだ。

ところが、pdftotextで抜き出したテキストをpolyglotで解析しようとすると、

pycld2.error: input contains invalid UTF-8 around byte 5302 (of 39198)

UTF-8じゃないのが入ってる」と言われてしまう。

じゃあ、Python上でPDFを抜き出してそれを渡そうと思い、textractをインストールすることにした。

#pip2.7 install textract

     #include <pulse/pulseaudio.h>
    compilation terminated.
    error: command 'gcc' failed with exit status 1

と怒られた。

pulseaudio-libs-devel が必要なのかな?

# yum install pulseaudio-libs-devel

してから再度実行するとインストールできたっぽい。

しかし、結局同じ結果に。うーん。

制御コードを除く

ということで、制御コードを除く処理を加えてみた。 PHPで制御文字を取り除く - tohokuaikiのチラシの裏

したら、問題なくパースしてくれた。オッケー。

nginxから使う

php-fpmとnginxから使ってみると、nginxからみたpolyglot_dataが無いって言われるのでエラーになる。

エラーが出るところを解析する。自分の環境では、/usr/lib/python2.7/site-packages/polyglot/load.py を無理やり編集。

  if not path.isdir(p):
    if downloader.status(package_id) != downloader.INSTALLED:
      raise ValueError("This resource is available in the index "
                       "but not downloaded, yet. Try to run\n\n"
                       "polyglot download {}".format(package_id))

とあるのを

  if not path.isdir(p):
    if downloader.status(package_id) != downloader.INSTALLED:
      raise ValueError("This resource is available in the index "
                       "but not downloaded, yet. Try to run\n\n"
                       "polyglot download {}".path.join(p, os.listdir(p)[0]))

として、エラーを見る。

すると、/var/opt/rh/rh-nginx112/lib/nginx/polyglot_data/embeddings2/en が無いって言われてるっぽいんで、

# ln -s /home/tohokuaiki/polyglot_data /var/opt/rh/rh-nginx112/lib/nginx/

としてやった。もうほんと、とりあえずって感じ。そうすると動いた。

プログラミング教育について「総合的な学習の時間」の学習指導要領解説を読む

これが思ったより少ない。これだけの記述のために世の中が右往左往しているのだろうか・・・。どんだけプログラミングに抵抗があるんだろう。

そもそも学習指導要領とは

平成29年3月に発表された新指導学習要領「生きる力」
学習指導要領等:文部科学省 にある、小学校学習指導要領 (PDF:1303KB) がそれである。

で、これだと難しすぎるので、解説がある。
小学校学習指導要領解説:文部科学省
この解説の中にある、「第5章総合的な学習の時間第3の2(2)」とかは、先述の「小学校学習指導要領」のものであって、解説のそれではない。

プログラミング教育は「総合的な学習の時間」の中に含まれるのでこの解説書を読む。
総合的な学習の時間 (PDF:1405KB)

この中でプログラミング教育に含まれているのはP60~の数ページに過ぎない。

小学校学習指導要領

P163にあるこれだけである。

(9) 情報に関する学習を行う際には,探究的な学習に取り組むことを通して,情報を収集・整理・発信したり,情報が日常生活や社会に与える影響を考えたりするなどの学習活動が行われるようにすること。第1章総則の第3の1の(3)のイに掲げるプログラミングを体験しながら論理的思考力を身に付けるための学習活動を行う場合には,プログラミングを体験することが,探究的な学習の過程に適切に位置付くようにすること。

その他、理科の箇所に「プログラミングを体験しながら」とあるのみ。

小学校学習指導要領解説 「総合的な学習の時間編」

この小学校学習指導要領のP163の1段落を説明したもの。

P59~P61の3ページのみである。

現代社会は高度に情報化した社会と言われている。多様で大量な情報が,瞬時に世界に広がる。また,身の回りには様々な情報があふれ,それらを適切に処理し活用する資質・能力の育成が求められている。このような時代に,総合的な学習の時間において,横断的・総合的な課題として情報に関する課題を扱い,その課題を探究的な学習の過程を通して取り組んでいくことには大きな意義がある。ここでは,探究的な学習に取り組むことを通してとあるように,電話,FAX,コンピュータ(タブレット型端末を含む),校内LAN,インターネット,デジタルカメラなどの情報手段を活用する必然性が伴う学習活動を行うことが重要であり,その過程において,情報手段の操作の習得も自然と行われるようにすることが望まれる。

情報を収集・整理・発信したりすることについては,本章の2(3)においても述べたように,探究的な学習の目的に応じて,図書やインターネットを活用したり,適切な相手を見付けて問合せをしたりして,学習課題に関する情報を幅広く収集し,それらを整理・分析して自分なりの考えや意見をもち,それを探究的な学習の目的に応じて身近な人にプレゼンテーションしたり,インターネットを使って広く発信したりするような,コンピュータや情報通信ネットワークなどを含めた多様な情報手段を,目的に応じて効果的に選択し活用する学習活動のことを指している。

情報が日常生活や社会に与える影響を考えたりすることについては,総合的な学習の時間の学習課題の例として,探究的な学習の過程において,情報技術の進化によって日常生活や消費行動がどのように変化したのか,社会がどのように豊かになったのかといったことを取り上げることが考えられる。同時に,日常生活にどのような新たな危険や困難がもたらされているのか,社会にどのような新しい問題が起きているのかを考えることも重要である。これらの情報技術の進化が我々の生活や社会にもたらす恩恵と問題を考えることを通して,今後の情報技術の進化に併せて,自分たちは将来,どのような生活を送り,どのような社会を築くことが望まれるのか,将来にわたる自分の生き方を見つめ考える契機とすることが大切である。あわせて,児童自身が情報を収集・整理・発信する活動を通して,未成年であっても情報社会の一員として生活しているという自覚を促し,発60信情報に責任をもつなどの意識をもたせる必要もある。

その中で,自分自身が危険に巻き込まれないことや情報社会に害を及ぼさないことなどの情報モラルについても,機を見て丁寧に指導する必要がある。例えば,電子掲示板を用いてみんなで調べたことを教え合うような学習活動では,相手を中傷するような書き込みが時折見られることがある。そのような場面を捉えて,なぜそれがいけないのか,どのようなことに発展する可能性があるのかなどを討論するようなことが考えられる。このように情報モラルを取り扱う場合には,児童自らの具体的で身近な素材を取り上げ,情報に関わる際の望ましい姿勢や態度,ならびに情報活用の方法などについて,自分のこととして見つめ直し考えさせることを通して,情報モラルを確実に身に付けさせることが望まれる。

プログラミングを体験しながら論理的思考力を身に付けるための学習活動については,第1章総則の第3の1の(3)のイに掲げられているとおり,総合的な学習の時間のみならず,算数科や理科をはじめとして各教科等の特質に応じて体験し,その意義を理解することが求められている。なお,プログラミングを体験しながら論理的思考力を身に付けるための学習活動を,どの教科等において実施するかということについては,各学校が教育課程全体を見渡し,プログラミングを体験する単元を位置付ける学年や教科等を決定していく必要がある。

そこでは,子供たちに,プログラミングにより意図した処理を行うよう指示することができるということを体験させながら,身近な生活でコンピュータが活用されていることや,問題の解決には必要な手順があることに気付き,発達の段階に即して論理的思考力を育成し,コンピュータの動きをよりよい人生や社会づくりに生かそうとする態度を涵養することが挙げられる。

プログラミングを体験しながら論理的思考力を身に付けるための学習活動とは,子供たちが将来どのような職業に就くとしても,時代を超えて普遍的に求められる力としての「プログラミング的思考」の育成を目指すものであり,プログラミングのための言語を用いて記述する方法(コーディング)を覚え習得することが目的ではない。「プログラミング的思考」とは,自分が意図する一連の活動を実現するために,どのような動きの組み合わせが必要か,どのように改善していけばより意図した活動に近づくのかということを論理的に考えていく力の一つである。このような思考力は,プログラミングに携わる職業を目指す児童にだけ必要な力ではなく,どのような進路を選択し,どのような職業に就くとしても,これからの時代において共通に求められる力であると考えられる。

特に総合的な学習の時間においては,プログラミングを体験しながら論理的思考力を身に付けるための学習活動を行う場合には,プログラミングを体験することだけにとどまらず,情報に関する課題について探究的に学習する過程において,自分たちの暮らしとプログラミングとの関係を考え,プログラミングを体験しな61がらそのよさや課題に気付き,現在や将来の自分の生活や生き方と繋げて考えることが必要である。例えば,プログラミングを体験しながら,生活を便利にしている様々なアプリケーションソフトはもとより,目に見えない部分で,様々な製品や社会のシステムなどがプログラムにより働いていることを体験的に理解するようにすることが考えられる。

例えば,カプセルトイの販売機とジュースの自動販売機を比べてみる。カプセルトイの販売機に比べ,ジュースの自動販売機は何が起きているのか分からない。お金を入れボタンを押すことで,選んだジュースとおつりが出る。自動販売機の中では何が起きているのだろう。子供たちは自動販売機の中で「プログラム」が動いていることを知り,身近な生活の中には,プログラムで動いていると想像されるものがたくさんあることに気付く。ここでジュースの自動販売機の中で起きていることをプログラミングする体験を取り入れることによって,プログラムは「機械の中にあるもの」,「機械に人間が考えた動きをさせるための命令であること」,「効率的に,順序立てた命令文の積み重ねであること」などを理解する。身近にプログラムで動いているものに関心をもった児童は,電気・水道・公共交通機関などのライフラインを維持管理するためにもプログラムが働いていることや,AI(人工知能)やビッグデータの活用,ロボットの活用によって,私たちの生活がより快適になり効率的になっていることにも気付いていくことが考えられる。

それらのプログラムの恩恵だけではなく,プログラムを悪用したコンピュータウイルスやネット詐欺などの存在にも触れることで,様々な新たな技術が開発され自分たちの身近な存在になる一方,「人間らしさとは何か」,「人間にしかできないこととは何か」,「人間としてどのように暮らしていけばいいのだろうか」など,自分の生き方を考え直すことも期待できる。

この展開例からも分かるように,総合的な学習の時間においてプログラミングを体験することは,それが探究的な学習の過程に適切に位置付けられていることが欠かせない。

またプログラミングを体験しながら論理的思考力を身に付けるための学習活動を行う場合にあっても,全ての学習活動においてコンピュータを用いてプログラミングを行わなければならないということではない。児童の発達段階や学習過程を考慮し,命令文を書いた紙カードを組み合わせ並べ替えることによって,実行させたいプログラムを構成したり,指令文を書いて他者に渡して,指令どおりの動きをしてもらえるかどうかを検証したりするなど,具体物の操作や体験を通して理解が深まることも考えられる。

  • 身近な生活でコンピュータが活用されていることや,問題の解決には必要な手順があることに気付いてもらうこと。
  • 時代を超えて普遍的に求められる力としての「プログラミング的思考」の育成を目指すものであり,プログラミングのための言語を用いて記述する方法(コーディング)を覚え習得することが目的ではない。
  • プログラミングを体験しながらそのよさや課題に気付き,現在や将来の自分の生活や生き方と繋げて考えることが必要
  • 目に見えない部分で,様々な製品や社会のシステムなどがプログラムにより働いていることを体験的に理解するようにすること

CentOS7のfirewalldでsshdをポートを変更して特定IPアドレスから許可する

色々と検索したけど、なんか微妙な感じだったのでまとめる。
結果的に[CentOS] firewalldで特定のサービスを特定のIPアドレスに対して許可する - Qiitaが一番適切だった。

firewalldは基本的に許可してないものは全部Drop

CentOS7では最初からpublicがActiveなゾーンとして設定されている。

#  firewall-cmd --list-all-zones
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: dhcpv6-client ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

手順

まずサービスの自体の追加

ポートを任意のモノ(2222とか)にしたサービスを設定する。
サービスの設定というけれども、これは単純にポートの設定。

ファイルを直接操作する。これはコマンドが無いのかな?

#cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh-2222.xml

反映のためのreload

# firewall-cmd --reload

次に許可するリストのためのZoneを作成する。

# firewall-cmd --permanent --new-zone=custom

このゾーンはリロード時にActiveなゾーンになる。

削除する場合は

# firewall-cmd --permanent --delete-zone=custom

あるいは、/etc/firewalld/zones/custom.xml があるのでそのファイルを削除。

新規で作ったzoneに新規で作ったサービスを追加

#  firewall-cmd --permanent --add-service=ssh-2222 --zone=custom

これで、このzoneはこのサービス(ポート)に対して接続を許可することになった。

そのZoneが使えるIPアドレスを指定

# firewall-cmd --permanent --add-source=192.168.1.125 --zone=custom

このあたり、先ほど作られたzoneファイルを直接編集しても大丈夫。

最後にDefaultのsshをpublicから削除してリロード

# firewall-cmd --permanent --remove-service=ssh --zone=public

これで通常のsshは外部から通らなくなる。(まだ設定は反映されてないので操作はできる)

# firewall-cmd --reload

vsftpdでuse_localtimeをYESにした場合、時刻が9時間ずれると言われた件でuse_localtimeオプションとは何であるかを考察した

結論から

言うと、このuse_localtimeをYESにすると9時間のズレはなくなるよっていう記事はわんさか見たけど、自分の場合は逆でuse_localtimeをNOにしたら直ったのである。

症状は・・・

vsftpdで、use_localtimeをYESにすると9時間のズレが起こる。

情報

サーバーのタイムゾーンJST日本標準時)。サーバーでtimedatectlを実行すると下記の通り。

$ timedatectl
      Local time: 金 2017-12-22 10:48:17 JST
  Universal time: 金 2017-12-22 01:48:17 UTC
        RTC time: 金 2017-12-22 01:48:16
       Time zone: Asia/Tokyo (JST, +0900)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

WinSCPでログインして、ファイルを置くとこうなる。

一見問題ないように見える。先ほどのtimedatectlと同じ時刻を返している(1分違うけどな)。

9時間遅い…

が、サーバー側でlsコマンドを打つとこうなる。

$ ls -l
合計 4
-rw-r--r-- 1 tohokuaiki tohokuaiki 1 12月 22 01:49 hoge

あれっ?9時間遅い?これが症状。

chrootしてるからか?と思ったけど違うみたい

vsftpd - use_localtime
とか見ると、chrootしてると/etc/localtimeを見られないので・・・というエントリーがたくさんあったけど、chrootを外しても症状は同じだったのでこれではないようだ。

WinSCPで通信ログを見てみる。

でログチェックをONにしてファイルでやり取りを見てみる。

use_localtimeとは?

そもそもuse_localtimeとは何かというと、vsftpd.confには以下のように書いてある。

If enabled, vsftpd will display directory listings with the time
in  your  local  time  zone.  The default is to display GMT. The
times returned by the MDTM FTP command are also affected by this option.

この「in your local time zone」というののyourって誰やねん?サーバ側かFTPクライアント側かっていうのがってのが分からない。

普通に考えるとFTPでつなぐクライアント側だと思うのだけど、さてWinSCPのログを見るとタイムゾーンの情報なんて全く送っていない。以下、接続した時点でのWinSCPのログ。

.Working directory: F:\WinSCPPortable
.Process ID: 18424
.Command-line: "F:\WinSCPPortable\WinSCP.exe" 
.Time zone: Current: GMT+9 (東京 (標準時)), No DST
.Login time: 2017年12月22日 10:57:33
.--------------------------------------------------------------------------
.Session name: tohokuaiki@vsftpd.example.com(FTP) (Site)
.Host name: vsftpd.example.com (Port: 21)
.User name: tohokuaiki (Password: Yes, Key file: No, Passphrase: No)
.Transfer Protocol: FTP
.Ping type: Dummy, Ping interval: 30 sec; Timeout: 15 sec
.Disable Nagle: No
.Proxy: None
.Send buffer: 262144
.UTF: Auto
.FTPS: None [Client certificate: No]
.FTP: Passive: Yes [Force IP: Auto]; MLSD: Auto [List all: Auto]; HOST: Auto
.Local directory: C:\Users\itoh\Documents, Remote directory: /work/20171222, Update: Yes, Cache: Yes
.Cache directory changes: Yes, Permanent: Yes
.Recycle bin: Delete to: No, Overwritten to: No, Bin path: 
.Timezone offset: 0h 0m
.--------------------------------------------------------------------------
.Session upkeep
.Connecting to vsftpd.example.com ...
.Connected with vsftpd.example.com. Waiting for welcome message...
<220 (vsFTPd 3.0.3)
>USER tohokuaiki
<331 Please specify the password.
>PASS ***********
<230 Login successful.
>SYST
<215 UNIX Type: L8
>FEAT
<211-Features:
< EPRT
< EPSV
< MDTM
< PASV
< REST STREAM
< SIZE
< TVFS
<211 End
.Connected
.Got reply 1 to the command 1
.--------------------------------------------------------------------------
.Using FTP protocol.
.Doing startup conversation with host.
>PWD
<257 "/" is the current directory
.Got reply 1 to the command 16
.Changing directory to "/work/20171222".
>CWD /work/20171222
<250 Directory successfully changed.
.Got reply 1 to the command 16
.Getting current directory name.
>PWD
<257 "/work/20171222" is the current directory
.Got reply 1 to the command 16
.Session upkeep
.Retrieving directory listing...
>TYPE A
<200 Switching to ASCII mode.
>PASV
<227 Entering Passive Mode (160,16,132,242,238,127).
>LIST -a
.Connecting to 160.16.132.242:61055 ...
.Data connection opened
<150 Here comes the directory listing.
.Data connection closed
.drwxr-xr-x    2 1000     1000         4096 Dec 22 10:49 .
.drwx------    7 1000     1000         4096 Dec 22 10:49 ..
.-rw-r--r--    1 1000     1000            1 Dec 22 01:49 hoge
<226 Directory send OK.
.Directory listing successful
.Got reply 1 to the command 2
.Detecting timezone difference...
.Retrieving file information...
>PWD
<257 "/work/20171222" is the current directory
>CWD /work/20171222/hoge
<550 Failed to change directory.
>TYPE I
<200 Switching to Binary mode.
>SIZE /work/20171222/hoge
<213 1
>MDTM /work/20171222/hoge
<213 20171222014942
.Retrieving file information successful
.Got reply 1 to the command 1024
.Timezone difference of -9 detected using file /work/20171222/hoge (Listing: 2017-12-21T16:49:00.000Z, UTF: 2017-12-22T01:49:00.000Z)
...;D;0;1899-12-30T09:00:00.000Z;0;"" [0];"" [0];---------;0
.hoge;-;1;2017-12-22T01:49:00.000Z;1;"1000" [0];"1000" [0];rw-r--r--;0

ログで違うなーと思ったのは

use_localtime=YES

Timezone difference of -9 detected using file /work/20171222/hoge (Listing: 2017-12-21T16:49:00.000Z, UTF: 2017-12-22T01:49:00.000Z)

use_localtime=NO

Timezone difference of -9 detected using file /work/20171222/hoge (Listing: 2017-12-21T07:49:00.000Z, UTF: 2017-12-21T16:49:00.000Z)

となっているところ。しかし、これはWinSCPから送信している情報ではない。サーバーから受け取っている情報である。

考察

こう考えると都合はあう。

おそらく、use_localtimeのlocalとはFTPクライアントタイムゾーンであろう。ただし、これをどうやってvsftpdが受け取っているのかが分からない。

基本的にvsftpdはGMT世界標準時)でファイルのタイムスタンプを扱うようになっているらしい。なので、ファイルをFTPソフトアップする際に、クライアントが+9のJST日本標準時)であるなら、vsftpdはそのファイルをのタイムスタンプを-9してサーバー上に保存する。

ただ、今回のサーバーはそもそもサーバーのタイムゾーンJST日本標準時)なのである。そこで-9されてしまってはずれが生じてしまう。

そこで、use_localtime=NOとすることで私のケースではズレが収まった。

結論

サーバーのタイムゾーンJSTにしている場合、use_localtime=NOにしておく。

PHPで検索キーワードを「”」みたいなクォート区切りを有効にしたGoogleみたいな感じで抜き出す方法

Google

なにか "PHP function" で検索

みたいにすると、"PHP function"っていう文字列で検索してくれるじゃないですか。あのキーワードの取り出し方を知りたいなと思って検索してみたんですが、正規表現一発で取れるものとかなくってしょうがないなーって感じでPHPで日和ってみた結果です。

<?php
function getSearchKeywords($text) 
{
    $keywords = array();
    while (preg_match('/"([^\"]+)"/', $text, $m)){
        $text = str_replace($m[0], '', $text);
        $keywords[] = $m[1];
    }
    return array_filter(array_merge($keywords, explode(" ", $text)));
}
$text = 'aaa "bbb ccc" ddd eee "fff" ggg "hhh iii jjj" kkk';
var_dump(getSearchKeywords($text)); 

 ↓

array(8) {
  [0]=>
  string(7) "bbb ccc"
  [1]=>
  string(3) "fff"
  [2]=>
  string(11) "hhh iii jjj"
  [3]=>
  string(3) "aaa"
  [5]=>
  string(3) "ddd"
  [6]=>
  string(3) "eee"
  [8]=>
  string(3) "ggg"
  [10]=>
  string(3) "kkk"
}

Confluence6.5をインストールした時にちょっと躓いたのでメモ

何気に6系は初めてだったりする。

いつものようにMySQLでデータベースを作成

mysql> create database confluence_demo_20171205 default character set utf8 ;
Query OK, 1 row affected (0.01 sec)

で、confluence/WEB-INF/classes/confluence-init.properties にconfluence.homeを設定してインストールWizardを走らせる。

MySQLを選択したら

Confluence needs a driver to connect to MySQL. You'll need to:
Download the MySQL driver
Drop the .jar file in /home/confluence/xxx/confluence/WEB-INF/lib
Restart Confluence and continue the setup process.

とか言われてしまった。しまったうっかりとお約束のMySQLJDBCを入れ忘れ。

Database JDBC Drivers - Atlassian Documentationに従って、MySQLJDBCドライバをOracleからいただきまして、/home/confluence/xxx/confluence/WEB-INF/libに入れてConfluenceの再起動

MySQLの設定

JDBCを入れて再度Wizardを進めると、Databaseのテスト接続で

Collation error
The database collation 'utf8_general_ci' is not supported by Confluence. You need to use 'utf8_bin'. Learn more

と言われてしまった。

https://confluence.atlassian.com/confkb/how-to-fix-the-collation-and-character-set-of-a-mysql-database-670958160.htmlによると、ちゃんとCollationまで気にして作らないとダメっぽい。

ということで、作り直し。

mysql> drop database confluence_demo_20171205;
Query OK, 0 rows affected (0.05 sec)

mysql> create database confluence_demo_20171205 default character set utf8 collate utf8_bin;
Query OK, 1 row affected (0.00 sec)

まだだめっす

Incorrect isolation level
Your database must use 'READ-COMMITTED' as the default isolation level. Learn more

なんだこれは?

なんか、MySQL3.5からおかしかったらしいけど、なんか使えてたのがダメにされたっぽい。

詳細はConfluence fails to start and throws 'MySQL session isolation level 'REPEATABLE-READ' is no longer supported' error - Atlassian Documentationのあたりを読んでくれということで、結局my.cnfの[mysqld]セクションに

[mysqld]
transaction-isolation=READ-COMMITTED

という一行を付け加えてMySQLを再起動。

更にダメッす

これで一応最後まで行った…。とおもいきや。

なんか日本語が化けている。Confluenceのページ内は大丈夫だけどスペース名とかページタイトルが文字化けしてる。

んー、なんだこれはと思ったら、そういえば、以前のConfluenceのJDBCのDSNを決めるときって「useUnicode=true&characterEncoding=UTF8」とかつけてたよな…と思い出す。

一旦全部アンインストールして、DBのセットアップで「Setup type」を「By connection string」にしてさっきの文字を追加してやる。

jdbc:mysql://localhost/confluence_demo_20171205?useUnicode=true&characterEncoding=UTF8

ここまでやってようやく成功。

JIRA(Atlassian cloud)で自分の作業した時間の記録を閲覧する方法

とりあえず前提として

毎日作業したら「作業ログ」にて作業時間を記録しておくこと。

これを集計するための方法を以下に。

メニューを開いて

に行けばなんとなくわかります。

今更ながらPHP7でローカルにPEARを入れた話

今どきPEARって…という感じですが、HTTP_Request2とか意外と使い慣れてるので便利なんですよ。いい加減ComposerでGuzzle使ってくれって話なのですが、ことによってはHTTP_Requestの方がちょろっとPHPCLIスクリプト書く分には楽ですよね。と。

インストールは今どきは、go-pear.phpじゃない

インストールはgo-pearを取るんだったなと思いおもむろに

$ wget http://pear.php.net/go-pear
$ php go-pear

と打ち込んだところ

Sorry!  Your PHP version is too new (7.0.19-1) for this go-pear.
Instead use http://pear.php.net/go-pear.phar for a more stable and current
version of go-pear, more suited to your PHP version.

Thank you for your coopertion and sorry for the inconvenience!

ありゃりゃ。ということで、

$ wget http://pear.php.net/go-pear.phar
$ php go-pear.phar

としてインストール開始。

インストール場所の設定

どこにインストールするかを聞かれる。
ひっそりとこっそりとどこぞやの作業量ディレクトリに入れたいので

Below is a suggested file layout for your new PEAR installation.  To
change individual locations, type the number in front of the
directory.  Type 'all' to change all of them or simply press Enter to
accept these locations.

 1. Installation base ($prefix)                   : /home/tohokuaiki/pear
 2. Temporary directory for processing            : /tmp/pear/install
 3. Temporary directory for downloads             : /tmp/pear/install
 4. Binaries directory                            : /home/tohokuaiki/pear/bin
 5. PHP code directory ($php_dir)                 : /home/tohokuaiki/pear/share/pear
 6. Documentation directory                       : /home/tohokuaiki/pear/docs
 7. Data directory                                : /home/tohokuaiki/pear/data
 8. User-modifiable configuration files directory : /home/tohokuaiki/pear/cfg
 9. Public Web Files directory                    : /home/tohokuaiki/pear/www
10. System manual pages directory                 : /home/tohokuaiki/pear/man
11. Tests directory                               : /home/tohokuaiki/pear/tests
12. Name of configuration file                    : /home/tohokuaiki/.pearrc

と聞かれるけれども、「1」を選択してインストール先を変更

Installation base ($prefix) [/home/tohokuaiki/pear] : /home/tohokuaiki/test/hoge/lib/pear

で、4~11が変更される。ここで12の.pearrcも変更しておこう。

さて、おもむろにリターンを押下してインストール開始!!・・・と思いきや

1-12, 'all' or Enter to continue:
Beginning install...
XML Extension not found

あれれ?

$ php -m|grep xml
libxml

libxmlではダメなんだ。debianなので

$ sudo apt-get install -y php7.0-xml

でインストール後、再度go-pear.pharを実行する。今度は問題なく進行。途中で

Would you like to alter php.ini </etc/php/7.0/cli/php.ini>? [Y/n] : n

と聞かれるが、ひっそりとPEARを入れているのでそんなだいそれたことはするはずもなく、男は黙って「n」を押下。

すると、

I will add a workaround for this in the 'pear' command to make sure
the installer works, but please look over your php.ini or Apache
configuration to make sure /home/tohokuaiki/test/hoge/lib/pear/share/pear is in your include_path.

と「このディレクトリをinclude_pathにいれるんやで」と注意してくれた。

うーむ、/home/tohokuaiki/test/hoge/lib/pear/share/pearpearが被ってしまったな。

PEARパッケージのインストール

/home/tohokuaiki/test/hoge/lib/pear/share/pear/bin/pearPEARコマンドである。

HTTP_Request2を入れたいので

$ /home/tohokuaiki/test/hoge/lib/pear/bin/pear install HTTP_Request2

とすると、

WARNING: channel "pear.php.net" has updated its protocols, use "pear channel-update pear.php.net" to update
Cannot install, php_dir for channel "pear.php.net" is not writeable by the current user

と出る。んー、php_dirが書き込みできない…ということで、そういえば、さっきのPEAR環境設定ファイル.pearrcを指定してコマンドを打ち直す。

こんな感じ。このコマンドの長さがひっそり感を醸し出していて非常に素晴らしい。

念のためchannel-updateをした後で

$ /home/tohokuaiki/test/hoge/lib/pear/bin/pear -c /home/tohokuaiki/test/hoge/lib/pear/.pearrc channel-update pear.php.net

パッケージのインストール

$ /home/tohokuaiki/test/hoge/lib/pear/bin/pear -c /home/tohokuaiki/test/hoge/lib/pear/.pearrc install HTTP_Request2

これで無事完了。

「生きがい」という言葉は日本語だけに存在する?

この記事でwithnews.jp

「以前、テレビの取材を受けた時、ちょっと意地悪な気持ちになって『生きがいって言葉は外国にはないし、言いませんよね。生きがいがなくても人間、ハッピーに生きていけるんじゃないですか』って答えたんです」

ってあって、Google翻訳で調べてみた。

言語 翻訳 日本語に再翻訳 Bingで再翻訳
英語 reason to live 生きる理由 生きる理由
フランス語 Je souhaite vivre 私は生きたい 私は生きたい
ロシア語 Я хочу жить 私は生きたい 行きたいです
スペイン語 Deseo vivir 私は生きたい 行きたいです
イタリア語 Voglio vivere 私は生きたい 行きたいです
ドイツ語 Ich möchte leben 私は生きたい 行きたいです
中国語 我想住 私は生きたい 行きたいです
韓国語 보람 やりがい やりがいのある
タイ語 ฉันต้องการที่จะมีชีวิตอยู่ 私は生きたい 生き続けたい
トルコ語 Yaşamak istiyorum 私は生きたい 住みたい
タガログ語 Nais kong mabuhay 私は生きたい 行きたいです
インドネシア語 Aku ingin hidup 私は生きたい 行きたいです
マレー語 Saya ingin hidup 私は生きたい 行きたいです
クメール語 ខ្ញុំចង់រស់នៅ 私は生きたい -
ハワイ語 Makemake au e ola 私は生き残りたい -
ギリシャ Θέλω να ζήσω 私は生きたい 行きたいです
スワヒリ語 Napenda kuishi 私は生きたい 生きていきたいと思います
サモア Ou te fia ola 私は生きたい -
マオリ E hiahia ana ahau ki te ora 私は救われたい -
ネパール語 म बाँच्न चाहन्छु 私は生きたい -
ヒンドゥ語 मैं जीना चाहता हूँ 私は生きたい 行きたいです
ヘブライ語 אני רוצה לחיות 私は生きたい 行きたいです

機械翻訳がどこまで正しいとかは言うつもりもないし検証する気力もないが、確かにそれにぴったり該当するような言葉は無いように思える。唯一韓国が近いものを感じさせるだけだ。

ちなみに、唯一韓国語であたった「やりがい」という言葉を翻訳してみると、

言語 翻訳 日本語に再翻訳 Bingで再翻訳
英語 Rewarding 報酬 やりがい
ドイツ語 Belohnung 報酬 報酬
中国語 奖励 報酬 報酬

と、これもどうもあまりありそうにない。行為に対する代償は、精神的なものでは無く有形なものであるということ。

言葉が無いということは概念も無いか非常に希薄であるということで、「人生は何かを成し遂げるために存在する」という考え方自体が日本人独特のものであるという証左であるかもしれない。何かを為すのが目的では無く、ただ生きるために生きるということ。

この言葉には「生きがい」というものは存在しない。

今更だけど、EthnaをPHP7にしてmysqliに対応した時のメモ

昔のコードのメンテナンスです(とか言ってみる

いい加減PHPも7になってEthnaはねーんじゃないの?って感じなのですが、これがまた意外と動くので使い続けたりしています。

ただ、preg_replaceのe修正子とmysql*系の関数が無くなったのでmysqli*に対応しないといけなくて、そのメモです。ちなみに、Ethna.phpをみると

/** バージョン定義 */
define('ETHNA_VERSION', '2.5.0-preview3');

とか書かれてたりします。

やらないといけないこと

  • preg_replaceのe修正子の削除
  • mysqliの適応
  • =&を=にする。(これは一括置換とか使って)

以下コードの羅列です。

etc/*****-ini.php

<?php
     'dsn' => 'mysql://user:pass@localhost/db_name',

 ↓

<?php
     'dsn' => 'mysqli://user:pass@localhost/db_name',

DB/Ethna_DB_PEAR.php

<?php

    function getInsertId()
    {
        if ($this->isValid() == false) {
            return null;
        } else if ($this->type == 'mysql') {
            return mysql_insert_id($this->db->connection);
        } else if ($this->type == 'sqlite') {


     function isValid()
     {
          if (is_null($this->db)
             || is_resource($this->db->connection) == false) {


    function quoteIdentifier($identifier)
    {
        if (is_array($identifier)) {
            foreach (array_keys($identifier) as $key) {
                $identifier[$key] = $this->quoteIdentifier($identifier[$key]);
            }
            return $identifier;
        }
            
        switch ($this->type) {
        case 'mysql':
            $ret = '`' . $identifier . '`';
            break;

 ↓

<?php

    function getInsertId()
    {
        if ($this->isValid() == false) {
            return null;
        } else if ($this->type == 'mysql') {
            return mysql_insert_id($this->db->connection);
        } else if ($this->type == 'mysqli') {
            return mysqli_insert_id($this->db->connection);
        } else if ($this->type == 'sqlite') {

     /* mysqli_connectはresourceではなく、Objectなので */
     function isValid()
     {
          if (is_null($this->db)
             || (is_object($this->db->connection) == false && is_resource($this->db->connection) == false)) {


    function quoteIdentifier($identifier)
    {
        if (is_array($identifier)) {
            foreach (array_keys($identifier) as $key) {
                $identifier[$key] = $this->quoteIdentifier($identifier[$key]);
            }
            return $identifier;
        }
            
        switch ($this->type) {
        case 'mysql':
        case 'mysqli':
            $ret = '`' . $identifier . '`';
            break;

preg_replaceのe修正子対応

Ethna_Controller.php

まとめて書く。

<?php
         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($action_name)) . '.' . $this->getExt('php');
         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($forward_name));
         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($forward_name)) . '.' . $this->getExt('php');

 ↓

<?php
         $postfix = preg_replace_callback('/_(.)/', function($m){ return strtoupper($m[1]);}, ucfirst($action_name));
         $action_name = substr(preg_replace_callback('/([A-Z])/', function($m){ return '_' . strtolower($m[1]);}, $target), 1);
         $r = preg_replace_callback('/_(.)/', function($m){ return '/' . strtoupper($m[1]); }, ucfirst($action_name)) . '.' . $this->getExt('php');
         $postfix = preg_replace_callback('/_(.)/', function($m){ return strtoupper($m[1]); }, ucfirst($forward_name));
         $r = preg_replace_callback('/_(.)/', function($m){ return '/' . strtoupper($m[1]); }, ucfirst($forward_name)). '.' . $this->getExt('php');

Plugin/Cachemanager/Ethna_Plugin_Cachemanager_Localfile.php

<?php
    function _escape($string)
    {
        return preg_replace('/([^0-9A-Za-z_])/e', "sprintf('%%%02X', ord('\$1'))", $string);
    }

 ↓

<?php
    function _escape($string)
    {
        return preg_replace('/([^0-9A-Za-z_])/', function($m){
            return sprintf('%%%02X', ord($m[1]));
        }, $string);
    }

Ethna_MailSender.php

<?php
                 $part['name'] = preg_replace('/([^\x00-\x7f]+)/e',
                     "Ethna_Util::encode_MIME('$1')", $part['name']); // XXX: rfc2231
                 $part['filename'] = preg_replace('/([^\x00-\x7f]+)/e',
                     "Ethna_Util::encode_MIME('$1')", $part['filename']);
              /* ... */
              $header[$i][] = preg_replace('/([^\x00-\x7f]+)/e', "Ethna_Util::encode_MIME('$1')", $value);

 ↓

<?php
                 if (preg_match_all('/([^\x00-\x7f]+)/', $part['name'], $m)){
                     foreach ($m[1] as &$v) $v = Ethna_Util::encode_MIME($v);
                     $part['name'] = str_replace($m[0], $m[1], $part['name']);
                 }
                 if (preg_match_all('/([^\x00-\x7f]+)/', $part['filename'], $m)){
                     foreach ($m[1] as &$v) $v = Ethna_Util::encode_MIME($v);
                     $part['filename'] = str_replace($m[0], $m[1], $part['filename']);
                 }
              /* ... */
              $header[$i][] = preg_replace_callback('/([^\x00-\x7f]+)/',function($m){ return Ethna_Util::encode_MIME($m[1]); }, $value);

Ethna_AppObject.php

<?php
         $table = preg_replace('/^([A-Z])/e', "strtolower('\$1')", $table);
         $table = preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $table);

 ↓

<?php
         if (preg_match_all('/^([A-Z])/', $table, $m)){
             foreach ($m[1] as &$v) $v = strtolower($v);
             $table = str_replace($m[0], $m[1], $table);
         }
         if (preg_match_all('/([A-Z])/', $table, $m)){
             foreach ($m[1] as &$v) $v = "_" . strtolower($v);
             $table = str_replace($m[0], $m[1], $table);
         }

=& newをつぶす

find lib app -name "*php"|xargs -I{} sed -i -e "s/ = &new/ = new/" {}
find lib app -name "*php"|xargs -I{} sed -i -e "s/ =& new/ = new/" {}

またdebianのvsftpdでハマったのでメモ

環境

uname -a
Linux www 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u2 (2017-06-26) x86_64 GNU/Linux

いつものように、chrootしてバーチャルユーザーでのログイン。

症状1.

vsftpでつなごうとするとConnection Refusedされる。

ローカルからFTPコマンドでたたいても同じ。

$ ftp
ftp> open localhost
ftp: connect to address ::1: Connection refused
Trying 127.0.0.1...
ftp: connect: Connection refused

チェック項目

こんな感じで調べて何とかなった。

modprobeでモジュールチェック

# lsmod|grep ftp
nf_nat_ftp             16384  0
nf_nat                 28672  1 nf_nat_ftp
nf_conntrack_ftp       20480  1 nf_nat_ftp
nf_conntrack          114688  5 nf_conntrack_ftp,nf_conntrack_ipv4,nf_nat_ftp,xt_conntrack,nf_nat

これがなかったら以下を入れておく。

# modprobe nf_conntrack_ftp
# modprobe nf_nat_ftp

バーチャルユーザーのshell

とりあえずバーチャルユーザーの割り当てにはftp_userというユーザーを作ってたんだけど、これのshellが/usr/sbin/nologinだと動作しない。

vsftpd.conf

この2つは両立させられないらしいので、ipv6は使わないようにする。

listen=YES
listen_ipv6=NO

ホームディレクトリを555パーミッションに設定

こんな感じでバーチャルユーザーを設定していると、

# more /etc/vsftpd.d/vsftpd_user_conf/vftpuser
guest_username=vftpuser
local_root=/home/example

/home/exampleのパーミッションを555にしなければならない。

が、最近のVSFTPDだと

allow_writeable_chroot=YES

で行ける。

パッシブモードで引っかかる

このあたりでお勉強

FTPのアクティブモードとパッシブモード + vsftpdでの設定方法|A Day In The Boy's Life

centos - How to configure vsftpd to work with passive mode - Server Fault

どうもポートを開放しないとダメっぽい。あれー、今まで動いてたのになぁ・・・。

ということで、vsftpd.confに以下を記述。

pasv_enable=YES
pasv_min_port=61050
pasv_max_port=61100

で、iptablesをこんな感じで。192.168.xxx.xxxは自ホスト。

iptables -A INPUT -p tcp -s 0.0.0.0/0 -d 192.168.xxx.xxx --dport 61050:61100 -j ACCEPT
iptables -A OUTPUT -p tcp -s 192.168.xxx.xxx --sport 61050:61100 -d 0.0.0.0/0 -j ACCEPT

毎回何かしら引っかかるね。。。

PHPでeBayのAPIを使う

結論から言うと、APIのReferenceがあるけどそんなのは見なくてPHPSDKがあるからそれを使ったらすぐだった。

とにかく、SandboxとProductionの両方を行き来するのでどっちがどっちかわからなくなるし、いつの間にかアカウントできてたのでこの2つがリンクするというのはどういう風なんだろう?メールアドレスが同じものをSandboxとProductionの両方で使って登録すればいいのかしら?

わかんなくなったら、My eBayにそれぞれログインする。 * http://my.ebay.com:tilte=Production用 My eBay * Sandbox用 My eBay

eBay developerアカウントを作る

Sandbox用とproduction用の2つ作成。

どちらから作り始めてもいいのだけど、sandbox用は「testuser_」がPrefixになる。

正直、ごちゃごちゃとやってたら2個できたし、その2個がリンクしてるのはどうやって作るのかはわからない。

こっちに書いてあった、テスト用Sandboxユーザーの登録方法

eBay Features - Testing in the Sandbox

  • Set up an email account for each Sandbox test user. A unique email is required for each test user you create.
  • On the eBay Developers Program home page, either create a developer’s account by clicking the Register link or login by clicking the eBay Developer Login button.
  • Sign in as a developer, using your eBay Developers Program username and password.
  • Go to the eBay Sandbox User Registration Tool page.
  • Create your test user by filling in the Username and Password fields. You can leave the other fields as is. Click the Create User button.

アプリケーションを登録してキーを取得

Applicationの名前を決めて、そのキーを取得

これも、SandboxとProductionの両方。

色々と求められるかもしれないけど、とりあえず携帯電話のSMSがあればなんとか突破できる。

Authトークンを取得

「Auth'n'Aut」と「hOAuth (new security)」と2つあるけど2つとも取得しておく。

この時、これでもかというくらいにログインをさせられるけど我慢する。

Return URLを登録

Authした時に返却されるURLとかプライバシーポリシーなんかのURLを登録しておく。別にeBayが後でクロールしてチェックとかはしないので適当なURLを入れておけばOK。

これも、当然SandboxとProductionの両方。

PHP eBay API SDKを取得

素晴らしいサイトから取得する。 devbay.net

面倒なので、githubから直接。
GitHub - davidtsadler/ebay-sdk-examples: Several examples of using the eBay SDK for PHP

composer.jsonがあるので、例によってcomposer installする。

ebay-sdk-examples/configuration.php の設定

大体見ればわかると思う。

ruNameというのが引っかかったのだけど、これは、Return URL Nameということで、先ほど登録してReturn URLの名称なのである。自分の場合、上のキャプチャ画面だとTakashi_ITOH-TakashiI-Junoef-fbnbhijtpとかいうのがそれにあたる。

sellerアカウントを設定

とりあえずこれで使えることは使える。Select系のAPIはProductionを使用する。登録系のAPIはSandboxを使用する。

登録系のAPIを使うにはSellerアカウントにしないといけない。

そのためのセットアップがここにある。Getting started selling on eBay

すると、My eBayのアカウントページで「Seller Account」というのが出てくるらしい。

で、これを当然のようにSandboxとProductionの両方で行う。

が、SandboxのSellerアカウントが設定できない・・・

上述のは、Productionの設定しか書いてなかった。いや、むしろテストするだけならSandboxの方だけで構わないのに、Productionの方だけSellerアカウントができてしまった。

Sandboxは登録しなくてもSellerテストできるんじゃん?って思ってSDKのサンプル商品登録を試してみたら

Error: You need to create a seller's account.
Before you can list this item we need some additional information to create a seller's account.

って出た。デスヨネー。

仕方ないのでSandboxのMy eBayに行くと How to Sell Items on eBay などと書いてあり、

  • Sign up for PayPal (optional)
  • Fill out the Sell Your Item form

とある。optionalとあるPaypalの方は後回しにして、「go to the Sell Your Item form.」ってリンクをクリックするとリンク切れ。左のメニューにSeller Dashboardというのもあるのでそっちにヒントあるかな?と思ってクリックしたらリンク切れ。切れそう。

My eBay Viewsをシラミ潰す。

とにかくここに「Seller Account」というのが出ればいいのである。

しかし、PaypalにLinkさせればいいのかと思いきや、昔作って使えてたPaypalのアカウントがペンディングになっててダメだったり、新しくPaypalのSandboxユーザーで作ればいいんじゃないかと

ここのリンクから作ろうとしたら、PaypalのSandboxユーザー登録で電話番号がアメリカ形式じゃないとかZIPコードがお前の入力した住所と一致してないとかアメリカの電話番号・郵便番号の形式や実在するものを調べて入力したけどダメ。およそSandboxっぽくない。もちろん国をUSから変更することはできない。

困り果ててもう一度検索

もしかすると、作ったSandboxユーザーを「Sellerにする」という発想が間違っているのではないか?・・・すると、こんなページが見つかった。

eBay Features - Testing in the Sandbox

The process of registering a test user is different than that for registering a real user in the Production environment. After you register a Sandbox test user, you can use it to buy and sell test items in the eBay Sandbox.

買う用と売る用の2つのテストアカウントが必要ですよと。

なんだそんなの・・・売る用(Seller)アカウントが作れないから困ってるんだっていうの・・・と思いつつ、この画面のここからもう一つ作ってみる。

そして、新しいSandboxユーザーでログイン・・・・。

・・・なんかSellerアカウントになってるね。

で、Tokenを2つ発行して、configuration.phpに設定・・・

vagrant@debian:~/work/ebay/ebay-sdk-examples$ php trading/03-add-auction-item.php
The item was listed to the eBay Sandbox with the Item number 110222380931

おぉっ!!!できてる!!!ホンマ、なんつー、落とし穴やねん。。。

Confluenceのアドオンで別のアドオンのComponentを使う場合の注意

結論から言うと、その別のアドオンを事前にインストールしてないとインストールに失敗しますよということ。

お互いのComponentを循環参照するようなアドオンは作れないってことですね。

こんな簡単なことに気づかずにはまりました。

IDE上ではpom.xml

<dependency>
    <groupId>jp.example.confluence.plugins</groupId>
    <artifactId>foo</artifactId>
    <version>1.00.01</version>
    <scope>system</scope>
    <type>jar</type>
    <systemPath>${basedir}\foo-1.00.01.jar</systemPath>
</dependency>

って記述してたのでIDE自身はjarを見れていたので。

エラーの感じとしては

こんなのが出てた

Caused by: org.osgi.framework.BundleException: Unresolved constraint in bundle jp.example.confluence.plugins.bar [250]: Unable to resolve 250.0: missing requirement [250.0] osgi.wiring.package; (osgi.wiring.package=jp.example.confluence.plugins.foo.component)

まぁ、そりゃそうですよね。

WordPressがローカルで立たなかった件

WordPress(正確にはMUだけど)のテストローカルを持ってこようとして、hostsファイルに 192.168.13.222 wp-testsite とか書いて、wp-config.phpのサイト名とか、DB内の所を上記のドメインで書き換えてインポート、さて見るぞ・・・と思ったらできない。 ??? いままで、WordPressの移動なんて何度もやってて、これしきの事で動かないわけがない・・・・。 でも、何度やってもダメ。 しょーがないから、調査してみた。1時間ほど。 ようやく見つけた。でも ...

[flex]PanelのStyle設定をするときに、他のファイルも影響する?

なんか、ハマりました。 PageSelector.mxml ってのに、 <?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" styleName="ModalDialog" > <mx:Style> .ModalDialog{ backgroundColo ...
To Top