Quantcast
Channel: 北風研究室
Viewing all 93 articles
Browse latest View live

CPI レンタルサーバーでPHP/MySql UTF-8 文字化け

$
0
0

先日から使用しているCPIレンタルサーバーへシステムを構築しているのですが、PHP でデータベースへ文字を登録すると文字化けしてしまうようです。

CPIレンタルサーバーのコントロールパネルからデータベースを新規作成する際に文字コードをUTF-8を指定したし、CREATE TABLE も UTF-8 を指定してあり、また PHP も mb_internal_encoding もファイルのエンコーディングも UTF-8 を指定していて、ローカルサーバーではきちんと動作しているシステムなので問題ないはずなのですが。

コントロールパネルのMySQL5.5管理画面より変数をしらべると character set 関連が ujis となっていました。

character set server: ujis
character set system: utf8
character set database: ujis
character set client: ujis

ではこれを編集して utf8 にすれば良いのだろうと思い、編集し保存すると権限がないてめ拒否されてしまった。

そこで、PHP/PDO からデータベースへ接続するときに charset=utf8 をつけてみると読み出し、登録も文字化けすることなく解決にいたりました。

http://php.net/manual/ja/ref.pdo-mysql.connection.php
http://php.net/manual/ja/mysqlinfo.concepts.charset.php


xargs で指定したコマンドに引数を渡す場合の工夫

$
0
0

xargsは標準入力を読み込み、それを引数として指定したコマンドを実行することができます。

例えば、以下のシェルスクリプトを実行すると指定したディレクトリにある更新日時が3日前のファイルを削除することができます。

$ find /tmp -mtime +3 -type f | xargs rm 

ところが find でファイルが見つからなかった場合、 rm のオペランドが不足ということになります。

rm: missing operand
Try `rm --help' for more information.

そこで、xargs に –no-run-if-empty というオプションを指定し実行することで、ファイルがない場合の対処ができます。

$ find /tmp -mtime +3 -type f | xargs --no-run-if-empty rm

以上です。

Asterisk TLSとSRTPでより安全なコール (パート1: TLS編)

$
0
0

Asterisk には TLS(Transport Layer Security)SRTP(Secure Real-time Transport Protocol) により通信を安全に行う方法があります。

TLS は Asterisk と SIPクライアントの SIP によるやりとりを暗号化し、SRTP は電話の音声ストリームを暗号化します。
ここでは CentOS6.x 上で Asterisk 13 をソースをビルドしてインストールされていることを前提として TLS と SRTP を有効にします。

1. 必要なパッケージのインストール
まず、暗号化にに必要な OpenSSL と LibSRTP をインストールします。

# yum install openssl openssl-devel
# yum install libsrtp libsrtp-devel

2. 自己署名証明書の作成
Asterisk で利用する自己署名証明書を生成するために Asterisk のソースディレクトリ下の contrib/scripts/に ast_tls_cert という便利なツールが付属しています。これで サーバー証明書とクライアント証明書を生成します。

# mkdir /etc/asterisk/keys
# ./ast_tls_cert -C foo.com -O "Foo Company" -d /etc/asterisk/keys
# ./ast_tls_cert -m client -c /etc/asterisk/keys/ca.crt -k /etc/asterisk/keys/ca.key -C foo.com -O "Foo Company" -d /etc/asterisk/keys -o sipclient

※ foo.com や "Foo Company" は実際のサーバーのFQDN またはIPアドレス、社名に置き換えてください。また、コマンドの途中でパスフレーズを数回入力するように促されますので適切なものを入力します。

コマンドを実行すると以下のファイルが /etc/asterisk/keys ディレクトリに生成されます。

# ll /etc/asterisk/keys 

asterisk.crt
asterisk.csr
asterisk.key
asterisk.pem
sipclient.crt
sipclient.csr
sipclient.key
sipclient.pem
ca.cfg
ca.crt
ca.key
tmp.cfg

3. sip.conf の変更
/etc/asterisk/sip.conf に以下を追加します。

tlsenable=yes
tlsbindaddr=0.0.0.0
tlscertfile=/etc/asterisk/keys/asterisk.pem
tlscafile=/etc/asterisk/keys/ca.crt
tlscipher=ALL
tlsclientmethod=tlsv1

続けて、SIPクライアント(例 2001 )の定義に “transport=tls” を追加します。

[2001]
type=peer
secret=password    ; this is NOT a secure password
host=dynamic
...(省略)...
transport=tls

4. クライアントの設定
Zoiper で TLS を有効にして確認します。

Preferences -> Accounts -> Advanced -> Use rport をチェック
Preferences -> Accounts -> Advanced -> Use TLS transport を選択
Preferences -> Advanced -> Security -> Extra CA certificate に sipclient.pem ファイルを選択
Preferences -> Advanced -> Security -> Protocol suite: から TLS v1 を選択
Preferences -> Advanced -> Security -> Disable certificate verification をチェック ※1

※1 このチェックボックスは DANGEROUS DO NOT USE! と表記されていますが、これをチェックしないと
Certificatge: Issuer untrusted/not found/not valid/wrong purpose というエラーになります。
自己署名の証明書のためと思われます。

Asterisk TLSとSRTPでより安全なコール (パート2: SRTP編)

$
0
0

前回はTLS設定で呼制御の暗号化を試みました。今回は通話の暗号化を試みます。

1. sip.conf の変更
/etc/asterisk/sip.conf のSIPクライアント(例 2001 )の定義に “encryption=yes” を追加します。

[2001]
type=peer
secret=password    ; this is NOT a secure password
host=dynamic
...(省略)...
transport=tls
encryption=yes

2. クライアントの設定
Zoiper で 暗号化 を有効にして確認します。

Preferences -> Accounts -> Advanced -> Use rport media をチェック
Preferences -> Accounts -> Advanced -> Use TLS with SDES SRTP を選択

以上です。

通話を開始すると Zoiper 上にカギのアイコンが表示されます。

また、今回の設定は Zoiper 以外にも AGEphone Business の評価版での試用をしました。
サポートの方に教えて頂きました AGEphone Business の設定方法は下記のとおりです。

Windows - AGEphone Biz3
TLS
- アカウント設定 > 接続種別 = TLS
- アカウント設定 > 全般の設定 > トランスポートにTLSを使う = ON
SRTP
- アカウント設定 > 全般の設定 > メディア暗号化(SRTP) = ON

PHPファイルエンコード変換

$
0
0

ファイルのエンコードを UTF-8 > Shift-JIS へ変換して、別ファイルへ出力するサンプルです。

ファイルの内容をすべて読み込んでエンコード変換する方法:

$srcfile = "/tmp/test.csv";
// 一時ファイルディレクトリにファイルを作成します
$dstfile = tempnam(sys_get_temp_dir(), 'prefix');

$contents = file_get_contents($srcfile);
$contents = mb_convert_encoding($contents, "Shift-JIS", "UTF-8");

$fp = fopen($dstfile , "w");

if ($fp) {
    fwrite($fp, $contents);
    fclose($fp);
}

上記方法だとファイルの内容が多いとメモリの使用上限に達することがあったので
少し改良した版:

$srcfile = "/tmp/test.csv";
// 一時ファイルディレクトリにファイルを作成します
$dstfile = tempnam(sys_get_temp_dir(), 'prefix');

$fpsrc = fopen($srcfile , "r");
$fpdst = fopen($dstfile , "w");

if ($fpsrc && $fpdst) {
    while(($chunk = fread($fpsrc, 1024))) {
        $chunk = mb_convert_encoding($chunk, "Shift-JIS", "UTF-8");
        fwrite($fpdst, $chunk);
    }
}
if ($fpsrc) {
    fclose($fpsrc);
}
if ($fpdst) {
    fclose($fpdst);
}

PHPテキストファイルの改行コード検出

$
0
0

テキストファイルの改行コードの検出方法を考えてみました。
単純にファイルの先頭を数MB 読んで改行コードを判定する方法です。
改行コードが混在してたり、バイナリファイルだと正しく検出できません。

echo detectEOL("/tmp/test.txt");

function detectEOL($file) {
    if (!file_exists($file)) {
        return null;
    }

    $fpsrc = fopen($file, "r");
    if (!$fpsrc) {
        return null;
    }

    $chunk = fread($fpsrc, 10*1024);
    fclose($fpsrc);

    if (empty($chunk)) {
        return null;
    }

    if (substr_count($chunk, "\r\n")) {
        return '\r\n';
    }

    if (substr_count($chunk, "\r")) {
        return '\r';
    }

    return '\n';
}

Asterisk キューのログを MySql へ格納

$
0
0

Asterisk 呼情報をODBCでMySqlへ格納する方法を以前やりました。

Asterisk CDR(呼情報)をODBCでMySqlへ格納
Asterisk CDR(呼情報)をODBCでMySqlへ格納(後記)

今回はAsterisk のキュー(待ち呼)のログをODBCでMySqlへ格納する方法を紹介します。

1. キューログ用のテーブルを作成

まずは、キューログ用に queue_log テーブルを作成します。

CREATE TABLE `queue_log` (
    `id`              bigint(255) unsigned NOT NULL AUTO_INCREMENT,
    `time`            varchar(26) NOT NULL DEFAULT '',
    `callid`          varchar(40) NOT NULL DEFAULT '',
    `queuename`       varchar(20) NOT NULL DEFAULT '',
    `agent`           varchar(20) NOT NULL DEFAULT '',
    `event`           varchar(20) NOT NULL DEFAULT '',
    `data`            varchar(100) NOT NULL DEFAULT '',
    `data1`           varchar(40) NOT NULL DEFAULT '',
    `data2`           varchar(40) NOT NULL DEFAULT '',
    `data3`           varchar(40) NOT NULL DEFAULT '',
    `data4`           varchar(40) NOT NULL DEFAULT '',
    `data5`           varchar(40) NOT NULL DEFAULT '',
    `created`         timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `queue` (`queuename`),
    KEY `event` (`event`)
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

2. extconfig.conf

/etc/asterisk/extconfig.conf にキューのログを格納する情報を記述します。

queue_log => odbc,asterisk,queue_log

第1パラメータ: ドライバ
第2パラメータ: res_odbc.conf のデータベースの設定情報 (DSN)
第3パラメータ: テーブル名

3. AsteriskとODBC接続の構成

AsteriskとODBC接続の構成は /etc/asterisk/res_odbc.conf で行います。

[asterisk]
enabled => yes
dsn => asterisk-connector
username => asterisk
password => asterpass
pre-connect => yes

4. ODBC設定の追加

Asteriskデータベースへの接続情報を /etc/odbc.ini へ追加します。

[asterisk-connector]
Description           = MySQL connection to 'asterisk' database
Driver                = MySQL
Database              = asterisk
Server                = localhost
UserName              = asterisk
Password              = asterpass
Port                  = 3306
Socket                = /var/lib/mysql/mysql.sock

5. 動作確認

Asterisk 再起動して動作確認します。

# service asterisk restart

キュー情報がMySqlへ格納されるか動作確認してみます。
ここでは、SIP #2501 がキューに入って #2502 が受けた結果を表示しています。

# mysql -u asterisk -p
Enter password: 

mysql> use asterisk;
mysql> select * from queue_log;

+----+----------------------------+--------------+------------+----------+----------------+------+-------+--------------+-------+-------+-------+---------------------+
| id | time                       | callid       | queuename  | agent    | event          | data | data1 | data2        | data3 | data4 | data5 | created             |
+----+----------------------------+--------------+------------+----------+----------------+------+-------+--------------+-------+-------+-------+---------------------+
| 18 | 2016-09-19 12:33:53.249160 | 1474256033.2 | queue_test | NONE     | ENTERQUEUE     |      |       | 2501         | 1     |       |       | 2016-09-19 12:33:53 |
| 19 | 2016-09-19 12:33:56.479286 | 1474256033.2 | queue_test | SIP/2502 | CONNECT        |      | 3     | 1474256033.3 | 3     |       |       | 2016-09-19 12:33:56 |
| 20 | 2016-09-19 12:34:06.721414 | 1474256033.2 | queue_test | SIP/2502 | COMPLETECALLER |      | 3     | 10           | 1     |       |       | 2016-09-19 12:34:06 |
+----+----------------------------+--------------+------------+----------+----------------+------+-------+--------------+-------+-------+-------+---------------------+
3 rows in set (0.01 sec)

参考:
http://stackoverflow.com/questions/30161384/asterisk-11-queue-log-to-mysql
http://work.mikeboylan.com/posts/2012/03/asterisk-queuelog-to-mysql.html
http://lists.digium.com/pipermail/asterisk-users/2009-September/237448.html

以上です。

CentOS FTP ログインでユーザーをルートフォルダに限定

$
0
0

エンドユーザーからファイルをFTPで受け取ってシステムに取り込む際、エンドユーザーには指定のフォルダ以外を見られたくないときの vsftp の設定。

1. vsftpd ユーザーのホームディレクトリの設定

まずは、ログインユーザーがログインした際のホームディレクトリを指定します。
ユーザー別の config ファイルを作成します。

ここでは、ユーザーを foo 、ユーザーのルートフォルダを /var/ftp_upload とします。フォルダの権限設定などはしてあるものとします。

# mkdir /etc/vsftpd/user_conf
# vi /etc/vsftpd/user_conf/foo
--- 以下の内容を入力します ---
local_root=/var/ftp_upload

以下のように user_config_dir の設定を変えて保存します。

# vi /etc/vsftpd/vsftpd.conf
--- 以下の内容を入力します ---
# User config dir
user_config_dir=/etc/vsftpd/user_conf

2. フォルダの移動させない (chrootさせない)

chroot_local_user が YES の場合、chroot_list は chroot させないユーザーのリストになります。ユーザーがフォルダを移動できないように、chroot_list を有効にして、ユーザーをリストに追加して、chroot_local_user を YES にします。

# vi /etc/vsftpd/vsftpd.conf
--- 以下の内容を入力します ---
chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list

ユーザー ‘foo’ を /etc/vsftpd/chroot_list に追加します。

# vi /etc/vsftpd/chroot_list
--- 以下の内容を入力します ---
foo

3. vsftpd 再起動

# service vsftpd restart

以上です。


Composer – PHP 依存関係管理

$
0
0

Composerは、PHPの依存性管理のためのツールです。プロジェクトが依存するライブラリを管理することができます。ライブラリの管理をプロジェクトごと行うことができるので、システム全体に影響することなく利用できます。

Composer のシステム要件は PHP 5.3.2以上ですが、PHP 5.3.4 以上を推奨しています。古いバージョンではエッジケースがあるかもしれないと注意事項があります。

1. インストール

Composer の公式ページのダウンロードのやり方を紹介します。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === 'e115a8dc7871f15d853148a7fbac7da27d6c0030b848d9b3dc09e2a0388afed865e6a3d6b3c0fad45c48e2b5fc1196ae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
php -r "unlink('composer-setup.php');"

インストールを確認してみます。

# which composer
/usr/local/bin/composer

これで composer がコマンドラインから使えるようになりました。

2. パッケージリスト

composer で利用できるパッケージは何があるのしょうか?
https://packagist.org/explore/ で確認できます。

私は ocr 関連のライブラリが必要なので “ocr” で検索しみるとたくさんできてきました。
今回は、”thiagoalessio/tesseract_ocr” を利用してみます。

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

パッケージのインストールには、”composer.json” というファイルに依存関係を記述してインストールフォルダに配置する必要があります。(https://packagist.org/ 参照)

今回利用する “thiagoalessio/tesseract_ocr” に composer.json の依存関係の記述があります。バージョンを “1.0.0-RC” から “0.2.1” に変更して使用します。
(追記:実は後でわかったことですが、1.0.0-RC のtesseract_ocr をインストールすると PHP 5.3.3 では文法エラーになる構文が使われています。)

# vi /project/lib/composer.json
--- 以下の内容を記述して保存 ---
{
    "require": {
        "thiagoalessio/tesseract_ocr": "0.2.1"
    }
}

そして、次に以下のコマンドを実行します。

# composer install
Your version of PHP, 5.3.3, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing thiagoalessio/tesseract_ocr (0.2.1)
    Downloading: 100%         

Writing lock file
Generating autoload files

「使用しているPHPのバージョンが 5.3.3 なのでCVE-2013-6420の影響があるので安全に証明書の確認ができないためアップグレードすることを強くお勧めします。」と注意されました。さっそくエッジケースに出くわしましたね。
取り急ぎ、影響ないので無視します。
(追記:xdebug を有効にして composer を実行しています。。。という注意は /etc/php.d/xdebug.ini の zend_extension=/usr/lib64/php/modules/xdebug.so の行頭に ; セミコロンを付けてコメントアウトすると xdebug が無効になります。)

インストールフォルダをみると以下のファイルとフォルダが出来ています。

composer.json
composer.lock
vendor
 - autoload.php

これがあればインストールは成功です。

5. PHPからパッケージの利用

インストールした “tesseract_ocr” は TesseractOCR のラッパーライブラリなので TesseractOCR をインストールします。

# yum install tesseract

https://packagist.org/packages/thiagoalessio/tesseract_ocr を参考にして画像をOCRしてみます。
画像はこちらを使用させていただきます。

# vi test.php
--- 以下の内容を記述して保存 ---
<?php
include "lib/vendor/autoload.php";

$tocr = new TesseractOCR("./text.jpeg");
$tocr-<setLanguage("eng");
$text = $tocr->recognize();

cho "Output ==>".PHP_EOL;
echo $text;
?>

さっそく実行してみます。

# php test.php
Tesseract Open Source OCR Engine v3.04.00 with Leptonica
Output ==>
The quick brown fox
jumps over the lazy
dog,

しっかり認識できています!

以上です。

PHP で複数ページのTIFFファイルをJPGに変換

$
0
0

ひとつのファイルに複数のページを格納したTIFFフォーマットの画像ファイルをそれぞれのページごとにJPGファイルとして保存する方法をPHPで実現します。

$path = "./src/foo.tif";
$dest = "./dst/";
$type = "jpg";

# ファイル名の取得
$extract = pathinfo($path);
$file = $extract['filename'];

# TIFF画像ファイルを読み込み
$images = new Imagick($path);

foreach($images as $i => $image) {
    $n = $i + 1;
    $image->writeImage("{$dest}{$file}_page{$n}.{$type}");
}
$images->clear(); 

例:

ソースファイル: foo.tif
出力ファイル: foo_page1.jpg  foo_page2.jpg foo_page3.jpg

以上です。

Microsoft ファイル チェックサム整合性検証ユーティリティ

$
0
0

ファイルをダウンロードして使用する際にチェックサムをチェックする習慣をつけるべく Windows 7 でチェックサムの算出できるツールを入手しました。

MSIV ユーティリティのダウンロード(英語):
https://www.microsoft.com/en-us/download/confirmation.aspx?id=11533

MSIV ユーティリティの説明:
https://support.microsoft.com/ja-jp/kb/841290

CentOS6 にMariaDB をインストール

$
0
0

CentOS 6.9/MySQL5.1 で動いているシステムを CentOS6.9/MariaDB 10.2 での動作検証を行うため、VMware 上でゲストOSとしてCentOS6.9 用意、MySQL はインストールせず、MariaDB をリポジトリでインストールします。

1. MariaDB のリポジトリ生成
こちらのサイトから「Downloads Setting up MariaDB Repositories」から以下の3ステップでリポジトリの内容が生成されます。

1. Choose a Distro
  => CentOS
2. Choose a Release
  -> CentOS 6 (x86_64)
3. Choose a Version
  -> 10.2 [Stable]

# MariaDB 10.2 CentOS repository list - created 2017-09-18 08:06 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.2/centos6-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

上記内容を /etc/yum.repos.d/MariaDB.repo というファイル名で保存します。

2. MariaDB のインストール
yum コマンドでインストール出来ます。

# yum install MariaDB-client MariaDB-server
# mysql -V
mysql Ver 15.1 Distrib 10.2.8-MariaDB, for Linux (x86_64) using readline 5.1

以上です。

Asterisk 13 FAX受信

$
0
0

Asterisk のFAX受信機能を使ってみます。

参考:http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/Fax_id290275.html

Asterisk はFAXを受信してTIFF画像へ保存します。FAXを受信するためにはspandspライブラリが必要になります。

Spandspライブラリのインストール

spandsp をビルドするためには libtiff ライブラリが必要です。

# yum install libtiff libtiff-devel

続いて、spandsp のビルド

# wget https://www.soft-switch.org/downloads/spandsp/spandsp-0.0.6.tar.gz
# tar zxvf spandsp-0.0.6.tar.gz
# cd spandsp-0.0.6
# ./configure
# make
# make install
# cat >> /etc/ld.so.conf.d/usrlocallib.conf   ※ ここで Enter タイプして、次行に続く
/usr/local/lib
ここで CTRL+d をタイプして保存します。
# ldconfig

Asterisk を Spandspライブラリとリビルド

spandsp をインストール後、Asteriskをリビルドします。

# cd /usr/local/src/asterisk-13.13.1    ※ ダウンロードした Asterisk のビルドによります
# ./configure
# make menuselect     ※ make menuselect で res_fax_spandsp を選択
Resource Modules / [*] res_fax_spandsp 
[Save & Exit]
# make
# make install
# service asterisk restart

FAX受信処理

extensions.conf で受信したFAXを TIFF 画像で保存します。

[faxin]
exten => _03XXXXXXX,1,NoOp(Faxin-${EXTEN})
 same => n,Set(FAXOPT(ecm)=yes)
 same => n,Set(FAXOPT(maxrate)=14400)
 same => n,Set(FAXOPT(minrate)=2400)
 same => n,Set(FAXOPT(modem)=v17,v27,v29)
 same => n,Set(TIFF=${EXTEN}-${UNIQUEID}.tif)
 same => n,ReceiveFax(/tmp/${TIFF})
 same => n,Hangup()
; Hangup.
exten => h,1,NoOp(Hangup in-fax)
 same => n,NoOp(FAX Status: ${FAXSTATUS})
 same => n,NoOp(FAXOPT(ecm): ${FAXOPT(ecm)})
 same => n,NoOp(FAXOPT(filename): ${FAXOPT(filename)})
 same => n,NoOp(FAXOPT(headerinfo): ${FAXOPT(headerinfo)})
 same => n,NoOp(FAXOPT(localstationid): ${FAXOPT(localstationid)})
 same => n,NoOp(FAXOPT(maxrate): ${FAXOPT(maxrate)})
 same => n,NoOp(FAXOPT(minrate): ${FAXOPT(minrate)})
 same => n,NoOp(FAXOPT(pages): ${FAXOPT(pages)})
 same => n,NoOp(FAXOPT(rate): ${FAXOPT(rate)})
 same => n,NoOp(FAXOPT(remotestationid): ${FAXOPT(remotestationid)})
 same => n,NoOp(FAXOPT(resolution): ${FAXOPT(resolution)})
 same => n,NoOp(FAXOPT(status): ${FAXOPT(status)})
 same => n,NoOp(FAXOPT(statusstr): ${FAXOPT(statusstr)})
 same => n,NoOp(FAXOPT(error): ${FAXOPT(error)})

備忘録として、受信したTIFF画像をPHP AGIを使ってJPEGへ変換したりするには ImageMagick が便利です。

ImageMagick のインストール

# yum install yum install ImageMagick ImageMagick-devel
# yum install php-pecl-imagick

Asterisk 13 FAX送信

$
0
0

Asterisk のFAX送信機能を使ってみます。

参考:http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/Fax_id290275.html

FAXを送受信するためにはspandspライブラリが必要になります。インストール手順はこちらの投稿を参考にしてください。

FAX送信はTIFF画像を送信します。ここでは送信したいTIFF画像があることを前提にAsteriskでFAX送信する方法をご紹介します。

FAX送信処理

FAX送信をするためには extensions.conf で以下の関数を呼ぶだけで行えます。

exten => send,1,SendFAX(/tmp/foo.tif,d)

これだけでは、送信する画像が固定なのでいろいろな画像を送信できるように処理を考えてみます。

[send-fax]
exten => send,1,NoOp(**** SENDING FAX ****)
 same => n,SendFAX(${FAXFILE},d)
 same => n,Hangup()
; Hangup.
exten => h,1,NoOp(Hangup send-fax)
 same => n,NoOp(${FAXSTATUS})
 same => n,NoOp(${FAXERROR})

送信するファイルを動的にセットするため、SendFAX関数のパラメータに FAXFILE という変数を指定しています。
では、次にこのファイル名を与えてFAX送信を実行させます。

コール ファイルでFAX送信実行

Asterisk にはコールファイルと呼ばれる構造化されたファイルを使うことで、自動でダイアル発信させる仕組みがあります。FAX送信を行うためこのコールファイルを利用します。

コールファイルの構文はこちらをご覧ください。

TIFF画像をFAX送信するためのコールファイルは以下の通りです。
/tmp/bar.tif 画像を 0311112222 へ送信する例です。

Channel: SIP/0311112222@account
Extension: send
Context: send-fax
Set: FAXFILE=/tmp/bar.tif

上記内容のコールファイルを /var/spool/asterisk/outgoing ディレクトリに配置すると、上記 extensions.conf の send-fax コンテキストの FAXFILE 変数に /tmp/bar.tif がセットされて send エクステンションの 実行されます。 つまり、/tmp/bar.tif 画像が 0311112222 へFAX送信されます。

※ 注意点は、/var/spool/asterisk/outgoing ディレクトリに直接コールファイルを作成せず、一時ディレクトリに作成した後、移動する必要があります。

複数の画像ファイルを順次FAXで送信するには、画像、送信先ごとににコールファイルを作成してスプールさせれば送信できます。電話回線のチャネル数制限がありますので、コールファイル数をうまく調整しないと送信エラーになる場合があります。

python で database クラスを作ってみる (Part 1)

$
0
0

python で Webクローラーをつくっているとどうしてもデータベースを参照、登録、更新が必要になります。MySQLにアクセスする方法少し楽して行えるように簡単なラッパークラスを作ってみたいと思います。

まずは、環境構築から。OS は CentOS7 です。データベースは mariadb をインストール済みです。
python は標準でインストールされている場合もありますが、念のため。

1. python 関連のインストール

# yum install python
# yum install python-devel
# yum install epel-release
# yum install python-pip
# yum install libxslt-devel libxml2-devel
# pip install beautifulsoup4
# pip install lxml

※ beautifulsoup4 はデータベースには必要はないですが、Webページを解析するのに必要なのでインストールしておきます。

2. MySql 関連のモジュールをインストール

# yum install gcc
# yum install mysql-devel
# pip install MySQL-python

python で database クラスを作ってみる (Part 2)

$
0
0

今回はクラスの定義とデータベースに接続、開く、閉じるところまでやっています。
クラス名は SimpleDB 、ファイルを simpledb.py とします。

コンストラクタで接続情報を渡し、open 関数で接続、カーソルを変数に格納しておきます。
close 関数でカーソルを閉じ、接続を閉じます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import MySQLdb
from collections import OrderedDict

class SimpleDB(object):
    __host       = None
    __user       = None
    __password   = None
    __database   = None
    __cursor    = None
    __connection = None

    def __init__(self, host, database, user, password):
        self.__host = host
        self.__database = database
        self.__user = user
        self.__password = password
    ## End def __init

    def open(self):
        con = MySQLdb.connect(self.__host, self.__user, self.__password, self.__database)
        self.__connection = con
        self.__cursor = con.cursor()
    ## End def open

    def close(self):
        self.__cursor.close()
        self.__connection.close()
    ## End def close
## End class

if __name__ == '__main__':
    dbconfig = {
        "host": "localhost",
        "database": "<your database>",
        "user": "<your username>",
        "password": "<your secure password>",
        }

    try:
        db = SimpleDB(dbconfig['host'], dbconfig['database'], dbconfig['user'], dbconfig['password'])

        db.open()
        db.close()
    except Exception as e:
        print e.args

python で database クラスを作ってみる (Part 3)

$
0
0

今回はテーブルにデータを挿入して、選択するところまでやります。

テーブルにデータを挿入するため、SimpleDB クラスに insert 関数を定義します。
引数には table: テーブル名、data: 挿入するデータを辞書型で渡します。
関数内では、引数を基にして “INSERT table INTO (field, field…) VALUES (value, value…)” という SQL 文を組み立てます。
値はプリペアードステートメントで渡すので %s で組み立てておいて self.__cursor.execute(sql, bind) でバインドしています。
INSERT が成功すると、最後の行ID self.__cursor.lastrowid を返し、失敗した場合は、None を返します。

class SimpleDB(object):
... (省略)...

    def insert(self, table, data):
        sql = "INSERT INTO %s" % table
        bind = []

        if data is not None and len(data) > 0:
            # Build fields
            delim = ""
            sql += " ("
            for field, value in data.iteritems():
                sql += "%s `%s`" % (delim, field)
                delim = ","
            sql += ")"

            # Build values
            delim = ""
            sql += " VALUES ("
            for field, value in data.iteritems():
                sql += "%s " % (delim)
                sql += "%s"
                bind.append(value)
                delim = ","
            sql += ")"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return last row ID.
            return self.__cursor.lastrowid
        return None
    ## End def insert

... (省略)...
## End class

次に、このテーブルからデータを取得するため、select 関数を定義します。
引数には table: テーブル名、conditions: 条件を指定する辞書型、sortby: ソートキー、sortdesc: ソート降順(True) を渡します。

関数内では、引数を基にして “SELECT * FROM table WHERE filed = value AND …” という SQL 文を組み立てます。
値はプリペアードステートメントで渡すので %s で組み立てておいて self.__cursor.execute(sql, bind) でバインドしています。
SELECT が成功すると、取得データを辞書型のリストで返し、失敗した場合は、None を返します。

class SimpleDB(object):
... (省略)...

    def select(self, table, conditions, sortby = None, sortdesc = False):
        result = None
        sql = "SELECT * FROM %s" % table
        bind = []

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if sortby is not None:
            sql += " ORDER BY %s %s" % (sortby, "DESC" if sortdesc else "ASC")

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            columns = self.__cursor.description
            result = []
            for row in self.__cursor.fetchall():
                item = {}
                for (index, value) in enumerate(row):
                    item[columns[index][0]] = value
                result.append(item)
        return result
    ## End def select

... (省略)...
## End class

python で database クラスを作ってみる (Part 4)

$
0
0

今回はテーブルのデータの更新と削除をやります。

データを更新するため、SimpleDB クラスに update 関数を定義します。
引数には table: テーブル名、更新データを辞書型、conditions: 条件を渡します。
関数内では、引数を基にして “UPDATE table SET field = value, …” という SQL 文を組み立てます。
UPDATE が成功すると、影響のあった行数を返し、失敗した場合は、None を返します。

    def update(self, table, data, conditions):
        sql = "UPDATE %s" % table
        bind = []

        if data is not None and len(data) > 0:
            # Build fields
            delim = ""
            sql += " SET "
            for field, value in data.iteritems():
                sql += "%s `%s` = " % (delim, field)
                sql += "%s"
                bind.append(value)
                delim = ","

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return rows affected.
            return self.__cursor.rowcount
        return None
    ## End def update

次に、このテーブルからデータを削除するため、delete 関数を定義します。
引数には table: テーブル名、conditions: 条件を指定する辞書型を渡します。
関数内では、引数を基にして “DELETE FROM table WHERE filed = value AND …” という SQL 文を組み立てます。
DELETE が成功すると、影響のあった行数を返し、失敗した場合は、None を返します。

    def delete(self, table, conditions):
        result = None
        sql = "DELETE FROM %s" % table
        bind = []

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return rows affected.
            return self.__cursor.rowcount
        return None
    ## End def delete
## End class

Python で例外が発生した場所を取得する

$
0
0

python でプログラミング中、try ~ except で例外を補足しますが、どのコードでその例外が発生したかを特定する方法がわからなかったので調べました。

https://docs.python.org/ja/2.7/library/sys.html

sys.exc_info() で返されるレースバックオブジェクトに設定された現在の行番号 tb_lineno で、例外が発生したコードの行番号がわかります。

import sys, os

try:
    # リスト インデックス範囲外
    for link in navi[0]:
        ・・・(省略)・・・
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print "%s, %s, %s" % (exc_type, fname, exc_tb.tb_lineno)

出力例:

<type 'exceptions.IndexError'>, 'test.py', 131

python で database クラスを作ってみる (Part 5)

$
0
0

今回は、複雑な SELECT 文に対応する select_custom 関数を追加して、簡単なテーブルでテストをしてシリーズ最後にします。

select_custom 関数は、引数 sql にパラメータ(%s)付きSQLステートメント、bind にパラメータの値リストを渡します。

class SimpleDB(object):
... (省略)...

    def select_custom(self, sql, bind):
        result = None

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            columns = self.__cursor.description
            result = []
            for row in self.__cursor.fetchall():
                item = {}
                for (index, value) in enumerate(row):
                    item[columns[index][0]] = value
                result.append(item)
        return result
    ## End def select_custom

... (省略)...
## End class

次に、SimpleDBクラスで、テーブルへデータ登録、更新、削除、そして選択してテストします。

テーブル名は t_test でテーブル定義は以下です。

CREATE TABLE t_test (
     id                int NOT NULL AUTO_INCREMENT
    ,name              varchar(260) NULL
    ,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

以下、テスト用コードです。

if __name__ == '__main__':
    dbconfig = {
        "host": "localhost",
        "database": "<your database>",
        "user": "<your username>",
        "password": "<your secure password>"
        }

    try:
        db = SimpleDB(dbconfig['host'], dbconfig['database'], dbconfig['user'], dbconfig['password'])
        db.open()

        # insert
        data = { 'name' : '山田 太郎' }
        rowid = db.insert("t_test", data)
        print "rowid: %d" % rowid


        # select
        conditions = { 'id' : rowid }
        res = db.select("t_test", conditions, "id", True)
        if res is not None:
            for r in res:
                print "select : %s, %s" % (r['id'], r['name'])

        # select_custom
        bind = [ rowid ]
        res = db.select_custom("select * from t_test where id = %s order by id", bind)
        if res is not None:
            for r in res:
                print "select_custom : %s, %s" % (r['id'], r['name'])

        # update
        conditions = { 'id' : rowid }
        data = { 'name' : '山田 花子' }
        rows_affected = db.update("t_test", data, conditions)
        print "%d 件更新" % rows_affected

        # select all
        conditions = None
        res = db.select("t_test", conditions, "id", True)
        if res is not None:
            for r in res:
                print "select : %s, %s" % (r['id'], r['name'])

        # delete
        conditions = { 'name' : '山田 花子' }
        rows_affected = db.delete("t_test", conditions)
        print "%d 件削除" % rows_affected

        # select all
        conditions = None
        res = db.select("t_test", conditions)
        if res is not None:
            for r in res:
                print "select : %s, %s" % (r['id'], r['name'])
        
        db.close()
    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        print "%s, %s, %s" % (exc_type, fname, exc_tb.tb_lineno)

実行結果:

# python simpledb.py 
rowid: 15
select : 15, 山田 太郎
select_custom : 15, 山田 太郎
1 件更新
select : 15, 山田 花子
1 件削除

参考までに:

SimpleDBクラスのソース: simpledb.py
(ファイル名を simpledb.py_.txt から “simpledb.py” に変更してください。)

Viewing all 93 articles
Browse latest View live