2.27.2011

Class of the variable-length array with initialization in VBScript

VBScript: クラスの初期化を行う可変長配列クラス

VBScriptでオブジェクトの可変長配列を実現するクラス。C++のSTL, vectorと名前を揃えている。
スカラー変数はサポート対象外。

Execute()メソッドを使って動的にコードを生成しているのが特徴。

・Class ObjectVector

   1: '// A class manages mutable object array.
   2: Const kErrorNotInitialized     = 1  '// Class name is not defined.
   3: Const kErrorAlreadyInitialized = 2  '// Class name is already defined.
   4: Const kErrorInvalidInitParam   = 3  '// Initialize parameter is invalid.
   5: Const kErrorOutOfRange         = 9  '// Specified index is out of range.
   6:  
   7: Class ObjectVector
   8:   Private items_()
   9:   Private size_
  10:   Private class_name_
  11:  
  12:   '// Constructor
  13:   Private Sub Class_Initialize()
  14:     ReDim items_(7)
  15:     size_ = 0
  16:     class_name_ = ""
  17:   End Sub
  18:  
  19:   '// Initializer
  20:   Public Sub Init(ByVal class_name)
  21:     If "" <> class_name_ Then
  22:       Call Err.Raise(kErrorAlreadyInitialized, "ObjectVector.Init()")
  23:     End If
  24:     class_name_ = class_name
  25:   End Sub
  26:  
  27:   '// Subscript sequence with checking.
  28:   Public Default Property Get at(ByVal index)
  29:     If IsOutOfRange(index) Then
  30:       Call Err.Raise(kErrorOutOfRange, "ObjectVector.at()")
  31:     End If
  32:     Set at = items_(index)
  33:   End Property
  34:  
  35:   '// Return length of sequense.
  36:   Public Property Get size()
  37:     size = size_
  38:   End Property
  39:  
  40:   '// Test if sequense is empty.
  41:   Public Property Get is_empty()
  42:     is_empty = (0 = size_)
  43:   End Property
  44:  
  45:   '// Return first element of sequense.
  46:   Public Property Get front()
  47:     Set front = Me.at(0)
  48:   End Property
  49:  
  50:   '// Return last element of sequense.
  51:   Public Property Get back()
  52:     Set back = Me.at(size_ - 1)
  53:   End Property
  54:  
  55:   '// Insert element at end.
  56:   '//   When the parameter "init_array" is not "Null",
  57:   '//   execute Init() function of the created object with "init_array".
  58:   Public Sub push_back(ByVal init_array)
  59:     If "" = class_name_ Then
  60:       Call Err.Raise(kErrorNotInitialized, "ObjectVector.push_back()")
  61:     End If
  62:     If Not (IsNull(init_array) Or IsArray(init_array)) Then
  63:       Call Err.Raise(kErrorInvalidInitParam, "ObjectVector.push_back()")
  64:     End If
  65:  
  66:     If UBound(items_) >= size_ Then
  67:       ReDim Preserve items_(size_ * 2 + 1)
  68:     End If
  69:     Call Execute("Set items_(size_) = New " & class_name_)
  70:     If IsArray(init_array) Then
  71:       Dim i, arr()
  72:       ReDim arr(UBound(init_array))
  73:       For i = 0 To UBound(arr)
  74:         arr(i) = "init_array(" & i & ")"
  75:       Next
  76:       Call Execute("Call items_(size_).Init(" & Join(arr, ",") & ")")
  77:     End If
  78:     size_ = size_ + 1
  79:   End Sub
  80:  
  81:   '// Erase element at end.
  82:   Public Sub pop_back()
  83:     If IsOutOfRange(size_ - 1) Then
  84:       Call Err.Raise(kErrorOutOfRange, "ObjectVector.pop_back()")
  85:     End If
  86:     Set items_(size_ - 1) = Nothing
  87:     size_ = size_ - 1
  88:   End Sub
  89:  
  90:   '// Test if index is valid.
  91:   Private Function IsOutOfRange(ByVal index)
  92:     IsOutOfRange = True
  93:     If Not IsNumeric(index) Then Exit Function
  94:     If (index >= size_) Or (index < 0) Then Exit Function
  95:     IsOutOfRange = False
  96:   End Function
  97: End Class
使い方は、Set obj = New ObjectVector
とインスタンスを生成してから、Init()関数で子クラス名を登録する。
push_back()関数はやや特殊な構文となっている。

push_back(Null) : 子クラスの生成のみ
push_back(Array(prm1, prm2, …)) : 渡されたパラメータで、子クラスのInit()関数を実行する。
  ※あらかじめ子クラスでのInit()関数の準備が必須
  ※子クラスのInit()関数が引数を取らない場合は、以下の構文を使用する
  push_back(Array())

・使用例

   1: '// Some class.
   2: Class Nullary
   3:   Public x
   4:   Public Sub Init()
   5:     x = 1
   6:   End Sub
   7: End Class
   8:  
   9: Class Unary
  10:   Public x
  11:   Public Sub Init(ByVal value)
  12:     x = value
  13:   End Sub
  14: End Class
  15:  
  16: Set obj1 = New ObjectVector
  17: Call obj1.Init("Nullary")
  18: obj1.push_back(Array())   ' Execute Nullary.Init().
  19: WScript.Echo obj1.back.x  ' Print obj1.at(0).x (=1)
  20:  
  21: Set obj2 = New ObjectVector
  22: Call obj2.Init("Unary")
  23: obj2.push_back(Null)      ' Not execute Init().
  24: obj2.push_back(Array(2))  ' Execute Unary.Init(2).
  25: WScript.Echo obj2(0).x    ' Print obj2.at(0).x (=Empty)
  26: WScript.Echo obj2(1).x    ' Print obj2.at(1).x (=2)

こちらへも掲載
http://www4.plala.or.jp/prj-m/files/vbscript/ObjectVector.vbs.txt

Difference between Nothing, Empty and Null in VBScript

VBScript: Nothing, Empty, Null の違いについて

Nothing: 参照先のないオブジェクト ⇒ Cで言えば、NULLポインタ

Empty: 初期化されていない変数の値、状況によって型が変わる ⇒ vbEmpty == 0

Null: 変数の値として使用できる、無効なデータ ⇒ vbNull == 1

   1: WScript.Echo vbEmpty, vbNull  ' 0 1
   2:  
   3: Dim a
   4: WScript.Echo IsEmpty(a), IsNull(a), IsObject(a) ' -1 0 0  // empty=>initialized
   5: Set a = WSH
   6: WScript.Echo IsEmpty(a), IsNull(a), IsObject(a) ' 0 0 -1  // object
   7: Set a = Nothing
   8: WScript.Echo IsEmpty(a), IsNull(a), IsObject(a) ' 0 0 -1  // still an object
   9: a = Null
  10: WScript.Echo IsEmpty(a), IsNull(a), IsObject(a) ' 0 -1 0  // null

参考:
http://www.atmarkit.co.jp/fwin2k/tutor/cformwsh05/cformwsh05_05.html

http://www.webmasterkb.com/Uwe/Forum.aspx/vbscript/3248/Nothing-Empty-Null

2.20.2011

Base64 converter with VBScript

VBScriptでBase64変換

自前で作ろうかと思ったが、探したら良質のスクリプトが既にあった。
なるほど、「Microsoft.XMLDOM」を利用すれば難しいことを考えずに済むのか。

http://www.vector.co.jp/soft/winnt/util/se385464.html

これを応用すれば、FTPが使えなくても、telnetだけでバイナリファイルの転送ができる。

・バイナリファイル
 ↓
・Base64でエンコード
 ↓
・telnet上でエンコードされた内容をファイルにする。
 たとえば、UNIX機であればviしてTeraTermから貼り付けするなり、
 TeraTermマクロで以下のようなコマンドを作ったり
  echo '~エンコードされた内容~' >> the_file_encoded.txt
   : ×行数
 ↓
・受け取り側で、ファイルの内容を見てBase64デコード

2.17.2011

TeraTerm – title format

TeraTerm - タイトル形式の変更

複数のTeraTermを立ち上げているとき、識別のためにウィンドウのタイトルを変えるが、デフォルトではタイトルが先頭ではなく中ほど(接続先ホスト・ポートの後)に表示されるため、タスクバーに並ぶと一見わかりづらい。

タイトル形式を変えるには、設定ファイルの「TitleFormat」行を変更する。

http://ttssh2.sourceforge.jp/manual/ja/setup/teraterm-win.html

TitleFormat=<format ID>

<format ID> には整数値を指定する。プログラムにより、ビット列として扱われる。

1ビット目 (1) TCP ホスト名もしくはシリアルポート番号を表示する
2ビット目 (2) セッション番号を表示する
3ビット目 (4)

VT/TEK を表示する

4ビット目 (8) <host/port> を先に表示する
5ビット目 (16) TCP ポート番号を表示する
6ビット目 (32) シリアルポートのボーレートを表示する

  以下に <format ID> の例を示します。

Format ID   タイトルの形式
0           <title>
1           <title> - <host/port>
2           <title> (session no.)
3           <title> - <host/port> (session no.)
4           <title> VT/TEK
5           <title> - <host/port> VT/TEK
6           <title> (session no.) VT/TEK
7           <title> - <host/port> (session no.) VT/TEK
13          <host/port> - <title> VT/TEK
29          <host:tcpport/port> - <title> VT/TEK
45          <host/port:baud> - <title> VT/TEK
61          <host:tcpport/port:baud> - <title> VT/TEK
省略時:
TitleFormat=5

2.11.2011

Command prompt emulation in a batch file

制限環境下でのコマンドプロンプトもどき

バッチファイルで、コマンドプロンプトのようなツールが作れないか試してみた。

・command.bat

   1: @echo off
   2: :loop
   3: set input=
   4: set /p input="%CD%>"
   5: %input%
   6: goto loop

コマンド実行後の改行とか、setした変数を使えないとか、構文エラーだと落ちるとか
本家には到底至らないものの、このシンプルさにしてまあまあ実用に耐えうるかと。

Catch the redirection error in UNIX shell

Solarisのkshで確認。

とあるファイルを初期化するのに、たとえばこんな関数を作ったとする。

   1: func() {
   2:   : > file
   3:   echo $?
   4: }

ここで、fileへの書き込みに失敗した場合、非ゼロの値がエコーされることを期待する。
しかし実際には何も表示されない。

$ func
ksh: file: 作成できません。
$

どうやら、組み込みコマンドのリダイレクションに失敗すると、その時点で関数から抜け出てしまうようである。

   1: func2() {
   2:   /bin/true > file
   3:   echo $?
   4: }
   5: func3() {
   6:   (:) > file
   7:   echo $?
   8: }

外部コマンドを使うか、サブシェルにすることで期待した結果を得ることができる。

$ func2
ksh: file: 作成できません。
1
$ func3
ksh: file: 作成できません。
1
$

2.10.2011

Creating a dummy file of a certain size

指定容量のダミーファイルを作成

ddコマンドを利用して、指定したサイズぴったりのファイルを作成する。

# dd if=/dev/zero of=./out_file bs=102400 count=1

ブロックサイズとカウントは適宜調整。

参考
http://www.itmedia.co.jp/help/tips/linux/l0606.html

2.08.2011

Printing newly-arrived mail in Samurize

Samurize 新着メール表示

コンフィグファイルのサンプルは以下のとおり。
http://www4.plala.or.jp/prj-m/files/samurize/mail.ini

「mogmail」を配備したパスを指定し、コンソールプログラムとして登録する。
ini-file オプションを使っているのは、ファイルのフルパスを指定するため。

「mail」のラベルには、使用するメーラーへのリンクを付けると便利。
(Input – Meter Linkage)

2.06.2011

mogmail – how to use

1. 適当な場所に「mogmail.exe」を配備
  Samurize\Scripts 配下で問題なし。
  http://www4.plala.or.jp/prj-m/files/mogmail/

2. コマンドプロンプトで以下のコマンドを実行
  > cd /d 配備先ディレクトリ

セットアップモードに入り、POP3サーバの通信設定を行う。
(斜体部分は各環境に合わせる)

  >mogmail.exe --setup
*** SETUP MODE ***
POP3 Server Address?: pop.hogehoge.jp
POP3 Server Port?[110]:
Username: hogehoge
Password: hogehoge
Testing connection...
OK.
Saved configuration to mogmail.ini.

カレントディレクトリに「mogmail.ini」が作成される

3. 「mogmail.exe」を実行すると、標準出力にメール情報が表示される
  より詳細なオプションは「mogmail.exe -–help」の実行結果を参照。

Samurize での設定方法は後日掲載。

mogmail – main function

mogmail - メイン処理

・pop3.h

   1: // Copyright (c) 2011 Mog Project. All rights reserved.
   2:  
   3: #ifndef _MOG_MAIL_POP3_H_
   4: #define _MOG_MAIL_POP3_H_
   5: #include <string>
   6: #include <boost/scoped_ptr.hpp>
   7: #pragma warning(disable : 4244)
   8: #include <boost/date_time/posix_time/posix_time.hpp>
   9: #pragma warning(default : 4244)
  10:  
  11: // Forward declaration.
  12: namespace mog {
  13: namespace net { class Connecter; }
  14: namespace mail {
  15:  
  16: // Mail headers struct.
  17: struct MailHeader {
  18:   boost::posix_time::ptime date;  // Universal Time, Coordinated
  19:   std::string from_name;
  20:   std::string from_address;
  21:   std::string subject;
  22: };
  23:  
  24: // A class manages POP3 communication.
  25: class POP3Client {
  26:  public:
  27:   POP3Client(
  28:       std::string const& server, std::string const& port,
  29:       std::string const& username, std::string const& password);
  30:   ~POP3Client();
  31:   MailHeader GetHeader(int index);
  32:   int num_mails() const { return num_mails_; }
  33:  
  34:  private:
  35:   boost::scoped_ptr<net::Connecter> connecter_;
  36:   bool is_active_;
  37:   int num_mails_;
  38:  
  39:   void Disconnect();
  40: };
  41:  
  42: }  // namespace mail
  43: }  // namespace mog
  44: #endif  // _MOG_MAIL_POP3_H_

・pop3.cc

   1: // Copyright (c) 2011 Mog Project. All rights reserved.
   2:  
   3: #include "pop3.h"
   4:  
   5: #include <boost/xpressive/xpressive.hpp>
   6: #include <boost/foreach.hpp>
   7: #include <boost/system/system_error.hpp>
   8: #include <boost/algorithm/string.hpp>
   9:  
  10: #include "base64.h"
  11: #include "character_converter.h"
  12: #include "connecter.h"
  13:  
  14: namespace xp = boost::xpressive;
  15:  
  16: namespace mog {
  17: namespace mail {
  18:  
  19: // Decoding MIME.
  20: std::string DecodeString(std::string str) {
  21:   xp::sregex const regex = "=?"
  22:       >> (xp::s1=+((xp::set='=', '+', '-', '/')|xp::alpha|xp::digit)) >> '?'
  23:       >> (xp::s2=+((xp::set='=', '+', '-', '/')|xp::alpha|xp::digit)) >> '?'
  24:       >> (xp::s3=+((xp::set='=', '+', '-', '/')|xp::alpha|xp::digit)) >> "?=";
  25:   xp::smatch match;
  26:   while (xp::regex_search(str, match, regex)) {
  27:     // Supportecd only ISO-2022-JP and Base64
  28:     if (!boost::iequals(std::string("ISO-2022-JP"), match[1].str())) { throw std::runtime_error("Character-set is supported only ISO-2022-JP."); }
  29:     if (!boost::iequals(std::string("B"), match[2].str())) { throw std::runtime_error("Encoding method is supported only BASE64."); }
  30:     str = match.prefix() +
  31:         mog::util::CharacterConverter::JIStoSJIS(mog::util::Base64::Decode(match[3])) +
  32:         match.suffix();
  33:   }
  34:   return str;
  35: }
  36:  
  37: ////////////////////////////////////////////////////////////////////////////////
  38: // POP3Client
  39: POP3Client::POP3Client(
  40:     std::string const& server, std::string const& port,
  41:     std::string const& username, std::string const& password)
  42:     : is_active_(false), num_mails_(0) {
  43:  
  44:   std::string line;
  45:   xp::sregex const reg_ok = xp::bos >> "+OK " >> (xp::s1=*~xp::_s) >> *xp::_;
  46:   xp::smatch match;
  47:  
  48:   try {
  49:     // Create session.
  50:     connecter_.reset(new ::mog::net::Connecter(server, port));
  51:     connecter_->ReadLine(&line);
  52:     if (!regex_match(line, reg_ok)) { throw std::runtime_error("Failed to create session."); }
  53:  
  54:     connecter_->WriteLine("USER " + username);
  55:     connecter_->ReadLine(&line);
  56:     if (!regex_match(line, reg_ok)) { throw std::runtime_error("Failed to user authentication."); }
  57:  
  58:     connecter_->WriteLine("PASS " + password);
  59:     connecter_->ReadLine(&line);
  60:     if (!regex_match(line, reg_ok)) { throw std::runtime_error("Failed to password authentication."); }
  61:  
  62:     connecter_->WriteLine("STAT");
  63:     connecter_->ReadLine(&line);
  64:     if (!regex_match(line, match, reg_ok)) { throw std::runtime_error("Failed to get status."); }
  65:     num_mails_ = boost::lexical_cast<unsigned int>(match[1]);
  66:   } catch(boost::system::system_error const& e) {
  67:     Disconnect();
  68:     throw e;
  69:   }
  70:  
  71:   is_active_ = true;
  72: }
  73:  
  74: POP3Client::~POP3Client() throw() { Disconnect(); }
  75:  
  76: MailHeader POP3Client::GetHeader(int index) {
  77:   MailHeader mh;
  78:  
  79:   try {
  80:     bool in_data = false;
  81:     std::string sect_title, sect_data;
  82:     connecter_->WriteLine("TOP " + boost::lexical_cast< std::string >(index) + " 0");
  83:  
  84:     for (;;) {
  85:       std::string line;
  86:       xp::smatch match;
  87:  
  88:       connecter_->ReadLine(&line);
  89:  
  90:       if (in_data) {
  91:         if (!line.empty() && std::isspace(line[0])) {
  92:           sect_data.append(line.substr(1));
  93:           continue;  // Data in multi-lines.
  94:         }
  95:         if ("Date" == sect_title) {
  96:           // s1:day(xx), s2:month(English), s3:year(xxxx), s4:hour(xx), s5:minute(xx), s6:second(xx)
  97:           // s7:sign of time zone(+-), s8:time zone hour(xx), s9:time zone minute(xx)
  98:           // Ex. Thu, 13 Jan 2011 17:16:37 +0900
  99:           if (xp::regex_match(sect_data, match, xp::sregex(
 100:               +xp::alpha >> ", " >> (xp::s1=+xp::_d) >> ' ' >> (xp::s2 = +xp::alpha) >> ' ' >> (xp::s3 = +xp::_d) >> ' ' >>
 101:               (xp::s4 = +xp::_d) >> ':' >> (xp::s5 = +xp::_d) >> ':' >> (xp::s6 = +xp::_d) >> ' ' >>
 102:               (xp::s7 = (xp::set = '+', '-')) >> (xp::s8 = xp::repeat<2>(xp::_d)) >> (xp::s9 = xp::repeat<2>(xp::_d)) >> *xp::_))) {
 103:             mh.date = boost::posix_time::time_from_string(
 104:                 match[3] + '-' + match[2] + '-' + match[1] + ' ' +
 105:                 match[4] + ':' + match[5] + ':' + match[6]);
 106:             // Adjust time zone to UTC.
 107:             mh.date -= boost::posix_time::hours(boost::lexical_cast<int>(match[7] + match[8]))
 108:               + boost::posix_time::minutes(boost::lexical_cast<int>(match[7] + match[9]));
 109:           }
 110:         } else if ("Subject" == sect_title) {
 111:           mh.subject = DecodeString(sect_data);
 112:         } else if ("From" == sect_title) {
 113:           if (xp::regex_match(sect_data, match, xp::sregex(
 114:             !xp::as_xpr('"') >> (xp::s1 = *~(xp::set='<', '"')) >> !xp::as_xpr('"') >> *xp::_s
 115:             >> !xp::as_xpr("<">> (xp::s2 = *~xp::as_xpr('>')) >> '>')))) {
 116:             mh.from_name = boost::trim_copy(DecodeString(match[1]));
 117:             mh.from_address = match[2];
 118:           } else {
 119:             mh.from_name = DecodeString(sect_data);
 120:           }
 121:         }
 122:         in_data = false;
 123:       }
 124:  
 125:       if (xp::regex_match(line, match, xp::sregex(xp::bos >> (xp::s1=+~xp::_s) >> ": " >> (xp::s2=*xp::_)))) {
 126:         sect_title = match[1];
 127:         sect_data  = match[2];
 128:         in_data = true;
 129:       } else if ( "." == line ) { break; }
 130:     }
 131:   }
 132:   catch(boost::system::system_error const& e) {
 133:     Disconnect();
 134:     throw e;
 135:   }
 136:   return mh;
 137: }
 138:  
 139: void POP3Client::Disconnect() throw() {
 140:   if (!is_active_) { return; }
 141:  
 142:   std::string line;
 143:   connecter_->WriteLine("QUIT");
 144:   connecter_->ReadLine(&line);
 145:  
 146:   connecter_.reset();
 147:   is_active_ = false;
 148: }
 149:  
 150: }  // namespace mail
 151: }  // namespace mog

・main.cc

   1: // Copyright (c) 2011 Mog Project. All rights reserved.
   2:  
   3: #pragma comment(lib, "cryptlib.lib")
   4:  
   5: #include <fstream>
   6: #include <vector>
   7:  
   8: #include <boost/scoped_ptr.hpp>
   9: #include <boost/foreach.hpp>
  10: #pragma warning(disable: 4512)
  11: #include <boost/program_options.hpp>
  12: #pragma warning(default: 4512)
  13: #include <boost/format.hpp>
  14: #include <boost/filesystem.hpp>
  15: #include <boost/date_time/local_time_adjustor.hpp>
  16: #include <boost/date_time/c_local_time_adjustor.hpp>
  17: #include <boost/xpressive/xpressive.hpp>
  18: #include <boost/mpl/if.hpp>
  19:  
  20: #pragma warning(push)
  21: #pragma warning(disable: 4083 100 127 189 244 505 512 615)
  22: #include <cryptopp/aes.h>
  23: #include <cryptopp/dh.h>
  24: #include <cryptopp/modes.h>
  25: #include <cryptopp/osrng.h>
  26: #pragma warning(pop)
  27:  
  28: #include "pop3.h"
  29: #include "base64.h"
  30:  
  31: using boost::posix_time::ptime;
  32: using CryptoPP::AES;
  33:  
  34: namespace po = boost::program_options;
  35: namespace xp = boost::xpressive;
  36:  
  37: namespace {
  38:  
  39: enum ExitCode { kOK = 0, kErrorSyntax, kErrorSetup, kErrorFetch, kErrorWriteResult, kErrorFatal };
  40: enum ExecutionMode { kModeFetch, kModeUsage, kModeSetup };
  41: struct ProgramOption {
  42:   ProgramOption() : mode(kModeFetch) {}
  43:   ExecutionMode mode;
  44:   int           out_count;
  45:   std::string   out_format;
  46:   std::string   ini_file_path;
  47: };
  48: struct ConnectOption {
  49:   ConnectOption()
  50:       :server_port("110") {}
  51:   std::string server_address;
  52:   std::string server_port;
  53:   std::string username;
  54:   std::string password;
  55: };
  56:  
  57: // Copy string with trailing null characters.
  58: void CopyWholeString(std::string const& src, std::string * dst) {
  59:   std::string::const_iterator it_src = src.begin();
  60:   std::string::iterator it_dst = dst->begin();
  61:   while (it_src != src.end() && it_dst != dst->end()) { *it_dst++ = *it_src++; }
  62: }
  63:  
  64: // AES encrypt and decrypt.
  65: template <bool IS_ENCRYPT>
  66: std::string FilterString(std::string const& in_value, std::string const& iv) {
  67:   std::string const kEncryptKey = "mogmog";
  68:   std::string encrypt_key(AES::DEFAULT_KEYLENGTH, 0);
  69:   std::string initialization_vector(AES::BLOCKSIZE, 0);
  70:   CopyWholeString(kEncryptKey, &encrypt_key);
  71:   CopyWholeString(iv, &initialization_vector);
  72:  
  73:   boost::mpl::if_<boost::mpl::bool_<IS_ENCRYPT>,
  74:       CryptoPP::CTR_Mode<AES>::Encryption,
  75:       CryptoPP::CTR_Mode<AES>::Decryption>::type processor;
  76:   processor.SetKeyWithIV(reinterpret_cast<byte const*>(
  77:       encrypt_key.c_str()), encrypt_key.size(), reinterpret_cast<byte const*>(initialization_vector.c_str()));
  78:  
  79:   std::string out_value;
  80:   CryptoPP::StreamTransformationFilter stf(processor, new CryptoPP::StringSink(out_value));
  81:   stf.Put(reinterpret_cast<byte const*>(in_value.c_str()), in_value.size());
  82:   stf.MessageEnd();
  83:   return out_value;
  84: }
  85:  
  86: // Parse command line.
  87: void ParseCommandLine(int argc, char * argv[], ProgramOption * option) {
  88:   po::options_description opt("Usage");
  89:   opt.add_options()
  90:       ("help", "Print this message.")
  91:       ("count", po::value<int>()->default_value(5), "Number of headers to get.")
  92:       ("format", po::value<std::string>()->default_value("[%m/%d %H:%M] %N <%A>%n %J"),
  93:           "Output format.\n"
  94:           "%y Last two digits of the year number\n"
  95:           "%Y Year number\n"
  96:           "%m Month number\n"
  97:           "%d Day number in the month\n"
  98:           "%H Hour number (24 hour system)\n"
  99:           "%M Minute number\n"
 100:           "%S Second number\n"
 101:           "%J Mail subject\n"
 102:           "%N Mail from-name\n"
 103:           "%A Mail from-address\n"
 104:           "%n New line\n"
 105:           "%% Character %")
 106:       ("ini-file", po::value<std::string>()->default_value("mogmail.ini"), "Path to the initial file.")
 107:       ("setup", "Remake the initial file.");
 108:   po::variables_map vm;
 109:   po::store(po::parse_command_line(argc, argv, opt), vm);
 110:   po::notify(vm);
 111:  
 112:   if (vm.count("help")) {
 113:     std::cout << opt << std::endl;
 114:     option->mode = kModeUsage;
 115:     return;
 116:   }
 117:   if (vm.count("setup")) { option->mode = kModeSetup; }
 118:   option->ini_file_path = vm["ini-file"].as<std::string>();
 119:   option->out_count = vm["count"].as<int>();
 120:   if (option->out_count <= 0) {
 121:     throw std::runtime_error("Count should be over 0.");
 122:   }
 123:   option->out_format = vm["format"].as<std::string>();
 124: }
 125:  
 126: // Read input from std::cin.
 127: bool GetLine(std::string const& prompt, std::string * line) {
 128:   std::cout << prompt;
 129:   std::getline(std::cin, *line);
 130:   return !line->empty();
 131: }
 132:  
 133: // Save configuration.
 134: void SaveConnectOption(std::string const& path, ConnectOption const& opt) {
 135:   std::ofstream ini_file(path);
 136:   if (!ini_file) { throw std::runtime_error("Failed to save ini-file."); }
 137:  
 138:   ini_file << "[POP3]\n";
 139:   ini_file << "SERVER=" << opt.server_address << "\n";
 140:   ini_file << "PORT=" << opt.server_port << "\n";
 141:   ini_file << "USERNAME=" << opt.username << "\n";
 142:   ini_file << "PASSWORD=" << mog::util::Base64::Encode(
 143:       FilterString<true>(opt.password, opt.username)) << "\n";
 144: }
 145:  
 146: // Load configuration.
 147: void LoadConnectOption(std::string const& path, ConnectOption *opt) {
 148:   std::ifstream ini_file(path);
 149:   if (!ini_file) {
 150:     throw std::runtime_error("Failed to open " + path +".\n" +
 151:                              "Execute '--setup' first.\n");
 152:   }
 153:  
 154:   std::string line;
 155:   std::string password_tmp;
 156:   xp::smatch match;
 157:   while (std::getline(ini_file, line)) {
 158:     if (xp::regex_match(line, match,
 159:         xp::sregex(xp::bos >> (xp::s1=+~(xp::set='=')) >> "=" >> (xp::s2=*xp::_)))) {
 160:       if ("SERVER" == match[1])   {
 161:         opt->server_address = match[2];
 162:       } else if ("PORT" == match[1]) {
 163:         opt->server_port = match[2];
 164:       } else if ("USERNAME" == match[1]) {
 165:         opt->username = match[2];
 166:       } else if ("PASSWORD" == match[1]) {
 167:         password_tmp = mog::util::Base64::Decode(match[2]);
 168:       }
 169:     }
 170:   }
 171:   opt->password = FilterString<false>(password_tmp, opt->username);
 172: }
 173:  
 174: // Interactive configuration setup.
 175: void SetupConfig(std::string const& path) {
 176:   ConnectOption conn_opt;
 177:   std::cout << boost::format("*** SETUP MODE ***") << std::endl;
 178:   std::string line;
 179:   for (;;) { if (GetLine("POP3 Server Address?: ", &conn_opt.server_address)) { break; } }
 180:   if (GetLine("POP3 Server Port?[110]: ", &line)) { conn_opt.server_port = line; }
 181:   for (;;) { if (GetLine("Username: ", &conn_opt.username)) { break; } }
 182:   for (;;) { if (GetLine("Password: ", &conn_opt.password)) { break; } }
 183:  
 184:   // Test connection.
 185:   std::cout << "Testing connection..." << std::endl;
 186:   boost::scoped_ptr<mog::mail::POP3Client> pop_client;
 187:   pop_client.reset(new mog::mail::POP3Client(
 188:       conn_opt.server_address, conn_opt.server_port, conn_opt.username, conn_opt.password));
 189:   pop_client.reset();
 190:   std::cout << "OK." << std::endl;
 191:  
 192:   // Save configuration.
 193:   SaveConnectOption(path, conn_opt);
 194:   std::cout << "Saved configuration to " << path << "." << std::endl;
 195: }
 196:  
 197: // Format result.
 198: std::string FormatResult(mog::mail::MailHeader const& mh, std::string const& format) {
 199:   std::string out_value;
 200:   tm local_time = boost::posix_time::to_tm(boost::date_time::c_local_adjustor<ptime>::utc_to_local(mh.date));
 201:  
 202:   bool flag_escaped = false;
 203:   for (std::string::const_iterator it = format.begin(); it != format.end(); ++it) {
 204:     if (flag_escaped) {
 205:       switch (*it) {
 206:         case 'y': out_value.append((boost::format("%02d") % (local_time.tm_year % 100)).str()); break;
 207:         case 'Y': out_value.append((boost::format("%04d") % (local_time.tm_year + 1900)).str()); break;
 208:         case 'm': out_value.append((boost::format("%02d") % (local_time.tm_mon + 1)).str()); break;
 209:         case 'd': out_value.append((boost::format("%02d") % local_time.tm_mday).str()); break;
 210:         case 'H': out_value.append((boost::format("%02d") % local_time.tm_hour).str()); break;
 211:         case 'M': out_value.append((boost::format("%02d") % local_time.tm_min).str()); break;
 212:         case 'S': out_value.append((boost::format("%02d") % local_time.tm_sec).str()); break;
 213:         case 'J': out_value.append(mh.subject); break;
 214:         case 'N': out_value.append(mh.from_name); break;
 215:         case 'A': out_value.append(mh.from_address); break;
 216:         case 'n': out_value.push_back('\n'); break;
 217:         case '%': out_value.push_back('%'); break;
 218:         default: throw std::runtime_error("Invalid output format: " + format);
 219:       }
 220:       flag_escaped = false;
 221:     } else if ('%' == *it) {
 222:       flag_escaped = true;
 223:     } else {
 224:       out_value.push_back(*it);
 225:     }
 226:   }
 227:   return out_value;
 228: }
 229:  
 230: }  // namespace
 231:  
 232: ////////////////////////////////////////////////////////////////////////////////
 233: // main
 234: int main(int argc, char * argv[]) {
 235:   ExitCode exit_code = kOK;
 236:   try {
 237:     ProgramOption option;
 238:  
 239:     // Parse command line options.
 240:     try {
 241:       ParseCommandLine(argc, argv, &option);
 242:     } catch(std::exception const& e) {
 243:       std::cout << "Error: " << e.what() << std::endl;
 244:       std::cout << "Check '--help' option.";
 245:       return kErrorSyntax;
 246:     }
 247:  
 248:     // Check mode.
 249:     switch (option.mode) {
 250:       case kModeUsage:
 251:         return kOK;
 252:       case kModeSetup:
 253:         // Save configuration file.
 254:         try {
 255:           SetupConfig(option.ini_file_path);
 256:         } catch(std::exception const& e) {
 257:           std::cout << "Error: " << e.what() << std::endl;
 258:           return kErrorSetup;
 259:         }
 260:         return kOK;
 261:       case kModeFetch:
 262:         break;
 263:       default:
 264:         assert(!"Unexpected mode.");
 265:     }
 266:  
 267:     int num_mails = 0;
 268:     std::vector<mog::mail::MailHeader> mail_header;
 269:     try {
 270:       // Load parameters.
 271:       ConnectOption conn_opt;
 272:       LoadConnectOption(option.ini_file_path, &conn_opt);
 273:  
 274:       // Connect to the POP3 server.
 275:       boost::scoped_ptr<mog::mail::POP3Client> pop_client;
 276:       pop_client.reset(new mog::mail::POP3Client(
 277:           conn_opt.server_address, conn_opt.server_port, conn_opt.username, conn_opt.password));
 278:       // Get mail count.
 279:       num_mails = pop_client->num_mails();
 280:       // Get headers.
 281:       for (int i = num_mails; i > num_mails - option.out_count && i > 0; --i) {
 282:         mail_header.push_back(pop_client->GetHeader(i));
 283:       }
 284:       // Disconnect from the server.
 285:       pop_client.reset();
 286:     } catch(std::exception const& e) {
 287:         std::cout << std::string("Error: ") + e.what();
 288:         exit_code = kErrorFetch;
 289:     }
 290:     // Format result.
 291:     try {
 292:       for (int i = 0; i < option.out_count; ++i) {
 293:         if (i < num_mails) {
 294:           std::cout << FormatResult(mail_header[i], option.out_format) << std::endl;
 295:         }
 296:       }
 297:     } catch(std::exception const& e) {
 298:         std::cout << std::string("Error: ") + e.what();
 299:         exit_code = kErrorWriteResult;
 300:     }
 301:   } catch(...) {
 302:     std::cout << "FATAL EXCEPTION!\n";
 303:     return kErrorFatal;
 304:   }
 305:   // system("pause");
 306:   return exit_code;
 307: }