braintag

公開してしまう備忘録

boost:asioを使って、ファイルをWebサーバにマルチパートでPOSTしてみる

boost:asioを使って、ファイルをWebサーバにPOSTしてみる
かなり昔に書いたコードです。
HDDの整理中で、誤ってソースを削除しそうなのでここにメモ。

制限

  1. ファイルはカレントディレクトリ
  2. ポートは80固定
  3. ドメインでなく、IPアドレス
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;
using namespace boost::asio;

int main()
{
	string filename="postfile.bin";//ポストしたいファイル名
	string ipaddress="192.168.1.1";//アップしたいサーバのIPアドレス


	ifstream f(filename.c_str(),ios::in | ios_base::binary);
	if (!f){
		cerr << "ファイルが開けません "<< endl;
		return -2;
	}

	//Content-Lengthに入れるためのファイルサイズの取得
	size_t filesize = (size_t)f.seekg(0, std::ios::end).tellg();
	f.seekg(0, std::ios::beg);  

	//boost::asioの出番
	ip::tcp::iostream s( ipaddress, "80" );
	
	if(s.fail()){
        cerr << "Webサーバが応答しません" << endl;
        return -1;
	}

	// 送信
	s << "POST /filerecv.htm HTTP/1.1\r\n";
	s << "Content-Type: multipart/form-data; boundary=---------------------------7d97d32d00e4\r\n";
	s << "Host: ";
	s << ipaddress;
	s << ":80";
	s << "Content-Length: ";
	s << dec << filesize + fileheader.length() +  filehooter.length();
	s << "\r\n";
	s << "Connection: Keep-Alive\r\n";
	s << "Cache-Control: no-cache\r\n";
//	s << "Authorization: Basic **********\r\n"; 認証がある場合
	s << "\r\n";

	s << "-----------------------------7d97d32d00e4\r\n";
	s << "Content-Disposition: form-data; name=\"BinaryCodeFile\"; filename=\"";
	s << filename;
	s << "\"\r\n";
	s << "Content-Type: application/octet-stream\r\n";
	s << "\r\n";


	char buffer[1024];
	int readsize = 0;
	while(!f.eof()){
		f.read(buffer,sizeof(buffer));
		s.write(buffer,f.gcount());
	}

	f.close();

	s << "\r\n-----------------------------7d97d32d00e4--\r\n\r\n";


	s << flush;  // バッファに溜めずに確実にネットワーク送信

	// 受信
	boost::regex reg("([0-9]{3})");

	string line;
	boost::smatch result;
	int statuscode = 0;

	getline(s, line);
	if(boost::regex_search(line, result, reg)){//ステータスコードがある場合
		statuscode = boost::lexical_cast<int>(result.str(1));//数値へ変換
	}
	if(statuscode != 200){
			return -3;
	}

	return 0;
}

ActiveXとjavascriptの連携

ActiveXjavascriptの関連のメモ。

1.javascript側からActiveXのメソッドをコール

objectタグにidをつけ、ActiveXのメソッドをそのまま呼び出すだけ。
ActiveXを作るときに命名したメソッド名がそのまま使える。

たとえばActiveXが以下のobjectだったとする。

<object id="baka" classid="省略">

で、ActiveX側のメソッド名が"aho"だった場合、javascriptからの呼び出し方は、

 baka.aho()

でオーケー。追加コードなし

2.javascriptからActiveXに値を渡す"プロパティ"
objectタグの中で

<param name="" value="">

で指定できる。
ActiveX側で、永続性の確保をするコードを忘れずに↓

	PX_String(pPX,	_T("URL"),				m_URL,					_T("http://"));	// url(cam1)

プロパティの追加は"メンバ変数"で。Get/Setは知らない。

3.ActiveX側のイベントを、javascript側で取得
ActiveX側で、コントロールを右クリックしてイベントの追加する。
javascript側のコードはこんな感じ。

<script for="ActiveXnoID" event="Eventer(fire)"
  alert("event=" + fire);>
</script>

そのメソッドをActiveXで呼び出したらイベントが発生し、
javascript側でalertダイアログが表示される。

boost::asioで、sip messegeを送信する

boost::asioを使って、UDPの送受信をしてみる.

受信待ちは5秒でタイムアウト

本当は受信を先に動かしておかなければいけないんだろうな

#include "StdAfx.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/system/error_code.hpp>

#include <Wincrypt.h>

class datagram_handler
{
	public:
	datagram_handler(io_service& ios):
		io_service_(ios),
		timer_(ios),
		socket_(ios, udp::endpoint(udp::v4(), 5060))
	{
		memset(data_,0,sizeof(data_));

		socket_.async_receive_from(
	        	boost::asio::buffer(data_, max_length), sender_endpoint_,
			boost::bind(
				&datagram_handler::handle_receive_from, this,
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred
			)
		);

		timer_.expires_from_now(boost::posix_time::seconds(5));//タイムアウト値のセット
		timer_.async_wait(boost::bind(&datagram_handler::close, this));//タイムアウトしたらclose
  	}

	void handle_receive_from(const boost::system::error_code& err,
		size_t /*length*/)
	{
		timer_.cancel();
		if (err){
			std::cerr << "Receive error: " << err.message() << "\n";
		}else{
			//ステータスコードの取得
			std::string line(data_);
			std::string statuscode;
			boost::regex reg_status("SIP/2.0 (\\d*)");
			boost::smatch result;
			if(boost::regex_search(line, result, reg_status)){//status
				statuscode = result.str(1);
			}else{
				std::cerr << "errror : status code not found.\n";
				return;
			}
			if(statuscode != "200"){
				std::cerr << "errror : status code not found.<< statuscode << "\n";
				return;
			}
			std::cout << "Successful receive\n";
		}
	}

	void close()
	{
		socket_.close();
	}

	private:
		io_service& io_service_;
		deadline_timer timer_;
		udp::socket socket_;
		udp::endpoint sender_endpoint_;
		enum { max_length = 512 };
		char data_[max_length];
};
std::string sjis2utf8(const std::string &sjisstr)
{
	//ShiftJIS  → UFT8 
	std::wstring wstr = ConvertMultiToWide(sjisstr); //UTF16に変換
	std::string strUtf8 = ConvertWideToMulti(wstr,CP_UTF8); //CP_UTF8に変換
	return strUtf8;
}
std::string MakeRandom()
{
	HCRYPTPROV hProv;
	BYTE buf[5];
	int i;

	CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);

	CryptGenRandom(hProv, sizeof(buf), buf);

	 std::string callid;
	 for(i=0;i<sizeof(buf);i++){
		callid += boost::lexical_cast<std::string>(static_cast<int>(buf[i]));
		if(i != sizeof(buf)-1){
			callid += '-';
		}
	 }
	CryptReleaseContext(hProv, 0);
	return callid;
}
void handle_send_to(const boost::system::error_code& err)
{
	if (err){
		std::cerr << "Send error: " << err.message() << "\n";
	}

}
int main(){
	try{
		std::string _s_ip("192.168.1.1");//SIPサーバのアドレス
		std::string _l_ip("192.168.1.100");//自分のIPアドレス gethostname+gethostbynameで取得
		std::string _l_num("3000");//自分の番号
		std::string _s_num("3001");//相手の番号
		std::string msg("こんにちは");//送信メッセージ
	

		boost::asio::io_service io_service;
		udp::socket s(io_service, udp::endpoint(udp::v4(), 0));

		udp::resolver resolver(io_service);
		udp::resolver::query query(udp::v4(),_s_ip, "5060");//SIPポート
		udp::resolver::iterator iterator = resolver.resolve(query);

		std::string utf8message=sjis2utf8(msg.c_str());//utf-8に変換しておく

		using namespace std;
		std::string sendmessege="MESSAGE sip:";
		sendmessege+= num;
		sendmessege+= "@";
		sendmessege+= _s_ip;
		sendmessege+= ":15060 SIP/2.0\r\n";
		sendmessege+= "Via: SIP/2.0/UDP ";
		sendmessege+= _l_ip;
		sendmessege+= ":5060;branch=z9hG4bK";
		sendmessege+= MakeRandom();//ランダム値生成
		sendmessege+= "\r\n";
		sendmessege+= "From: <sip:";
		sendmessege+= _l_num;
		sendmessege+= "@";
		sendmessege+= _s_ip;
		sendmessege+= ":5060>;tag=da3676\r\n";
		sendmessege+= "To: <sip:";
		sendmessege+= num;
		sendmessege+= "@";
		sendmessege+= _s_ip;
		sendmessege+= ":5060>\r\n";
		sendmessege+= "Call-ID: ";
		sendmessege+= MakeRandom();
		sendmessege+= "@";
		sendmessege+= _l_ip;
		sendmessege+= "\r\n";
		sendmessege+= "CSeq: 1 MESSAGE\r\n";
		sendmessege+= "Content-Type: text/plain; charset=utf8\r\n";
		sendmessege+= "Max-forwards: 69\r\nContent-Length: ";
		sendmessege+= boost::lexical_cast<string>(utf8message.length());
		sendmessege+="\r\n\r\n";
		sendmessege+=utf8message;

		size_t request_length = sendmessege.length();//strlen(request);

		//SIP Message 送信
		s.async_send_to(
			boost::asio::buffer(sendmessege, request_length), *iterator,
			boost::bind(
				&handle_send_to,
	       			boost::asio::placeholders::error
			)
		);
/
		//応答を受信
		datagram_handler dh(io_service);

		//実行
		io_service.run();

		s.close();


	}catch (std::exception& e){
		std::cerr << "Exception: " << e.what() << "\n";
	}
}

オープンソースのSIPライブラリ eXosipを VC9.0でコンパイルする

必要なもの

Microsoft Visual Studio 9.0

eXosip
http://savannah.nongnu.org/projects/exosip

osip
http://www.gnu.org/software/osip/

openssl
http://www.02.246.ne.jp/~torutk/cxx/openssl/install.html



ディレクトリ構造を以下のようにする
d:\sip\libeXosip2-3.3.0
d:\sip\osip [リネームしておく]
d:\lib\openssl

VCのインクルードファイルにd:\lib\opensslを追加しておく

あとは、
\libeXosip2-3.3.0\platform\vsnet\eXosip.slnをVCで開いてコンパイルするだけ

WindowsのADSIを使用して、LDAPサーバからアドレスデータを取得するコード

WindowsではADSI(winldap32.dll)というやつが用意されているので、これでLDAPのアクセスを行う。

LDAPサーバに格納したinetOrgPersonから、全アドレスの電話番号、名前、グループ名称を取得する

LDAPに格納されたデータは、base64化されていたり、文字コードutf-8だったりする(RFC)が、base64のデコードや、utf-8からの変換を、勝手にやってくれるので便利

wldap32.libをリンカに追加しておくこと

#include "StdAfx.h"
#include <winldap.h>
using namespace std;

int main(void)
{
	LDAP *ld;
	LDAPMessage* pMsg  = NULL;
	if ( ( ld = ldap_open( "192.168.1.1", LDAP_PORT ) ) == NULL ) {
		printf("ldap_open error");
		return -1;
	}

	const PCHAR SEARCH_BASE=	"ou=sample,o=example,dc=test,dc=com";
	const PCHAR  ATTRIBUTE_CN	= "cn";
	const PCHAR  ATTRIBUTE_Phone	= "telephoneNumber";
	const PCHAR  ATTRIBUTE_OU		= "ou";

	//タイムアウト
	l_timeval tm;
	tm.tv_sec = 0;
	tm.tv_usec = 5*1000*1000;

	// LDAPサーチ	
	if ( ldap_search_st( ld, SEARCH_BASE, LDAP_SCOPE_SUBTREE, 
                NULL, NULL, 0,&tm, &pMsg ) != LDAP_SUCCESS ) {
		ldap_perror(ld, "ldap_search_s");
		ldap_unbind ( ld );
		_error_msg = "ldap_search_st timeout";
		return -2;
	}
	if ( pMsg == NULL ) {
		_error_msg = "ldap_search_s returns NULL result.";
		ldap_unbind ( ld );
		return -3;
	}

	//カウント
	int nCount = ldap_count_entries( ld, pMsg );
	if ( nCount <= 0 ) {
		ldap_msgfree( pMsg );
		ldap_unbind (ld );
		return -4;
	}
	parray_address->reserve(nCount);//効率化

	// Get each entry
	LDAPMessage* pEntry;
	for ( pEntry = ldap_first_entry( ld, pMsg );
	      pEntry != NULL ;
	      pEntry = ldap_next_entry( ld, pEntry ) ) 
	{
		char** ppszPhone = NULL;
		char** ppszCn = NULL;
		char** ppszOu = NULL;

		ppszPhone = ldap_get_values( ld, pEntry, ATTRIBUTE_Phone );
		if(ppszPhone == NULL){
			continue;
		}else if(ppszPhone[0] == NULL){
			ldap_value_free( ppszPhone );
			continue;
		}

		ppszCn = ldap_get_values( ld, pEntry, ATTRIBUTE_CN );
		if(ppszCn == NULL){
			ldap_value_free( ppszPhone );
			continue;
		}else if(ppszCn[0] == NULL){
			ldap_value_free( ppszPhone );
			ldap_value_free( ppszCn );
			continue;
		}

		ppszOu = ldap_get_values( ld, pEntry, ATTRIBUTE_OU );
		if(ppszOu == NULL){
			ldap_value_free( ppszPhone );
			ldap_value_free( ppszCn );
			continue;
		}//グループは空を許可

		printf("telnumber = %s\n",ppszPhone[0]);
		printf("name = %s\n",ppszCn[0]);
		printf("group = %s\n",ppszOu[0]);


		//文字列解放
		if(ppszPhone){
			ldap_value_free( ppszPhone );
		}
		if(ppszCn){
			ldap_value_free( ppszCn );
		}
		if(ppszOu){
			ldap_value_free( ppszOu );
		}
	}

	//解放
	ldap_msgfree( pMsg );
	ldap_unbind ( ld );


	return 0;
}

Javascript正規表現オンラインチェッカー

すぐ忘れてしまい必死にググってもなかなか出てこないのに
懲りたのでメモる。

Regex Tester
http://regexpal.com/

下段にデータを入れ、上段に正規表現を記入する

matchした部分は、黄色くハイライトされ、わかりやすい。

一番いいところは、リアルタイムに結果がわかるので非常に助かる。

Trac 0.10.5で、BatchModifyPluginインストール後、カスタムクエリのリンクが消える

Trac 0.10.5の管理をまかされてしまった。

ちまちまチケットを変更するのが面倒なので、カスタムクエリの結果に対して、一気にチケットを修正できるBatchModifyPluginを入れ、こりゃあ便利だな、と思ってたところ、TICKET_BATCH_MODIFY権限が無い人からクレームが。

「カスタムクエリへのリンクが消えてるじゃないか、どうなってんの?」

一般権限で入ると、たしかにカスタムクエリへのリンクが無くなっている。
なんだろう。0.10.5の日本語版との相性だろうか。
ググッっても全く情報が出てこない。うちの環境だけだろうか。

どう設定を変更してもダメだったので、ソースを参照して自分で修正するしかないと観念した。

tracのプラグインどころか、pythonを読むのもいじるのはじめてだったので、カット&トライの繰り返し。(運用中にやるなよ)

batchmod/web_ui.pyを開き、26行目から

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return QueryModule(self.env).get_active_navigation_item(req)

    def get_navigation_items(self, req):
#        from trac.ticket.report import ReportModule 
#        if req.perm.has_permission('TICKET_VIEW') and \
#                not self.env.is_component_enabled(ReportModule):
#            yield ('mainnav', 'tickets',
#                   html.A('View Tickets', href=req.href.query()))
#        elif req.perm.has_permission('TICKET_BATCH_MODIFY'):
        yield ('mainnav', 'query',
               html.A('Custom Query', href=req.href.query()))

こんな感じにコメントアウト
一般権限でも無条件で表示させるようにした。

あとはpython setup.py installで終了。

めでたく一般権限でもカスタムクエリが表示できた。

ちゃんとBatchModify画面は表示されていないことも確認済み。