SylpheedでHTMLメールを表示する Windows編

Unix系OSではhtmlviewなるWebKitGTKを利用しているプラグインをSylpheed向けに過去に作りました。
多少インターフェースに難はありますが、HTMLメールを表示することができます。

が、このhtmlviewプラグインはWindows版を提供していません。

  • Sylpheed Windows版で使っているGTK+が2.10.14と古いためWebKitGTK+を使うにはちょっとつらそう
  • WebKitGTK+のバイナリの準備が面倒臭そう(試してない)

というのがその理由です。

GTK+のバイナリをごっそり入れかえてしまえばいいのかも知れませんが、プラグインとしてはそこまで要求するのはなんだかなぁというのがあります。

というわけで、その他の方法としてGtkHTMLによるHTMLメールの表示をやってみました。

GtkHTMLも依存関係がとても面倒臭いので自前でビルドしたりしているんですが、HTMLメールで送られてくるコミットメールの差分を表示するだけでこんなんなっているのでまだまだ改善が必要そうです。

GtkHTMLでHTMLな文字列をさらっと表示するためのサンプル

GtkHTML 3.x用。GtkHTML 4.xは未確認。

#include <string.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gtkhtml/gtkhtml.h>

static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data )
{
    g_print ("delete event occurred\n");
    return FALSE;
}

static void destroy( GtkWidget *widget,
                     gpointer   data )
{
    gtk_main_quit ();
}

static const gchar *html_string = "<html><body><h1>title</h1><p>Hello, GtkHTML!</p></body></html>";
int main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *html;

  gtk_init(&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  
  g_thread_init(NULL);

  g_signal_connect (window, "destroy",
		  G_CALLBACK (destroy), NULL);

  html = gtk_html_new();
  
  gtk_html_load_from_string(GTK_HTML(html),
			    html_string,
			    strlen(html_string));

  gtk_container_add(GTK_CONTAINER(window), html);

  gtk_widget_show_all(window);
  gtk_main();
  return 0;
}

mfiler4 1.1.1のコンパイルエラーに対処する

はじめに

mfiler4というファイラーがあります。

http://sourceforge.jp/projects/mfiler4/

mfiler4 1.1.1をUbuntu 12.04LTSの環境でコンパイルしようとしたときに、以下のようなコンパイルエラーに遭遇しました。
git HEADでも状況は同様でした。

-[3527]% LANG=C make
gcc -DSYSCONFDIR="\"/home/kenhys/etc/mfiler4/\"" -DDATAROOTDIR="\"/home/kenhys/share/doc/mfiler4/\"" -DSYSTEM_MIGEMODIR="\"/usr/share/cmigemo\"" -Isrc/ -I/home/kenhys/include -L/home/kenhys/lib -I . -I/usr/local/include -L/usr/local/lib -Werror   -c -o src/filer.o src/filer.c
src/filer.c: In function 'make_file_stat':
src/filer.c:1441:9: error: format '%d' expects argument of type 'int', but argument 4 has type '__nlink_t' [-Werror=format]
src/filer.c:1441:9: error: format '%d' expects argument of type 'int', but argument 4 has type '__nlink_t' [-Werror=format]
src/filer.c: In function 'cmdline_view_filer':
src/filer.c:2530:13: error: format '%d' expects argument of type 'int', but argument 5 has type '__nlink_t' [-Werror=format]
src/filer.c:2530:13: error: format '%d' expects argument of type 'int', but argument 5 has type '__nlink_t' [-Werror=format]
src/filer.c:2549:12: error: format '%d' expects argument of type 'int', but argument 5 has type '__nlink_t' [-Werror=format]
src/filer.c:2549:12: error: format '%d' expects argument of type 'int', but argument 5 has type '__nlink_t' [-Werror=format]
cc1: all warnings being treated as errors
make: *** [src/filer.o] Error 1

問題点とパッチ

64bit環境と、それ以外では型修飾子に指定すべきものが違う、というのが肝です。
そこで以下のようにしてみました。longかそうでないかで分岐します。

diff --git a/src/common.h b/src/common.h
index b724127..1b65350 100644
--- a/src/common.h
+++ b/src/common.h
@@ -38,6 +38,12 @@
 
 #define S_IXUGO (S_IXOTH | S_IXGRP | S_IXUSR)
 
+#if defined(__x86_64__)
+    #define FORMAT_HARDLINK "%3ld"
+#else
+    #define FORMAT_HARDLINK "%3d"
+#endif
+
 //////////////////////////////////////////////
 // main.c
 ///////////////////////////////////////////////
diff --git a/src/filer.c b/src/filer.c
index 2d03879..d52eaa8 100644
--- a/src/filer.c
+++ b/src/filer.c
@@ -1436,7 +1436,7 @@ static void make_file_stat(sFile* file, char* buf, int buf_size)
 
     char* env_nlink = getenv("VIEW_NLINK");
     if(env_nlink && strcmp(env_nlink, "1") == 0) {
-        snprintf(buf + strlen(buf), buf_size -strlen(buf), " %3d", file->mStat.st_nlink);
+        snprintf(buf + strlen(buf), buf_size -strlen(buf), " "FORMAT_HARDLINK, file->mStat.st_nlink);
     }
 
     char* env_owner = getenv("VIEW_OWNER");
@@ -2516,7 +2516,7 @@ void cmdline_view_filer()
         while(year > 100) year-=100;
         
         snprintf(buf2, 1024, 
-            "%s %3d %-8s %-7s%s %02d-%02d-%02d %02d:%02d %s"
+            "%s "FORMAT_HARDLINK" %-8s %-7s%s %02d-%02d-%02d %02d:%02d %s"
             //, "%s %3d %-8s %-7s%10lld %02d-%02d-%02d %02d:%02d %s"
             , permission, file->mLStat.st_nlink
             , owner, group
@@ -2534,7 +2534,7 @@ void cmdline_view_filer()
     }
     else {
         snprintf(buf, 1024,
-           "%s %3d %s%s%s %02d-%02d-%02d %02d:%02d %s -> %s"
+           "%s "FORMAT_HARDLINK" %s%s%s %02d-%02d-%02d %02d:%02d %s -> %s"
            //, "%s %3d %s%s%10lld %02d-%02d-%02d %02d:%02d %s -> %s"
            , permission, file->mLStat.st_nlink
            , owner, group

これともうひとつcursesがらみでもエラーになる部分がありました。
そちらはA_*がunsigned longとして定義されていることに由来するものです。

-[3508]% grep A_REVERSE /usr/include/**/*.h
/usr/include/curses.h:#define WA_REVERSE	A_REVERSE
/usr/include/curses.h:#define A_REVERSE	NCURSES_BITS(1UL,10)
/usr/include/ncurses.h:#define WA_REVERSE	A_REVERSE
/usr/include/ncurses.h:#define A_REVERSE	NCURSES_BITS(1UL,10)
/usr/include/ncursesw/curses.h:#define WA_REVERSE	A_REVERSE
/usr/include/ncursesw/curses.h:#define A_REVERSE	NCURSES_BITS(1UL,10)
/usr/include/ncursesw/ncurses.h:#define WA_REVERSE	A_REVERSE
/usr/include/ncursesw/ncurses.h:#define A_REVERSE	NCURSES_BITS(1UL,10)

こちらについても、パッチは似たような感じになります。

diff --git a/src/main.c b/src/main.c
index 8ebe03c..e95ac19 100644
--- a/src/main.c
+++ b/src/main.c
@@ -541,11 +541,11 @@ static void set_mfenv()
     setenv("nometa", buf, 1);
     snprintf(buf,128, "%d", 1);
     setenv("meta", buf, 1);
-    snprintf(buf,128, "%d", A_REVERSE);
+    snprintf(buf,128, "%ld", A_REVERSE);
     setenv("ma_reverse", buf, 1);
-    snprintf(buf,128, "%d", A_BOLD);
+    snprintf(buf,128, "%ld", A_BOLD);
     setenv("ma_bold", buf, 1);
-    snprintf(buf,128, "%d", A_UNDERLINE);
+    snprintf(buf,128, "%ld", A_UNDERLINE);
     setenv("ma_underline", buf, 1);
     snprintf(buf,128, "%d", COLOR_PAIR(1));
     setenv("ma_white", buf, 1);

まとめ

mfiler4を64bit環境でコンパイルするための方法を紹介しました。
それでは良いmfiler4ライフを。

VirtualBoxでゲストのファイルシステムが高負荷でread onlyにならないための方法

ホストOSがWindows 7でゲストOSがUbuntu 12.04という環境での話。
VirtualBoxのバージョンは4.2.4。


ゲストOSでビルドをぶんまわしていると、とたんにファイルシステムがread onlyになってしまう現象が頻繁に発生していた。

ぐぐってみたら既知の問題らしい。

High I/O causing filesystem corruption
https://www.virtualbox.org/ticket/10031


対策としてはVMの設定でストレージの項目にある「ホストのI/Oキャッシュを使う」のチェックを入れないというものがあった。

だが、それでは状況は改善されなかった。


最終的には以下にあるようにフラッシュする間隔を調整することにした。

http://www.virtualbox.org/manual/ch12.html#ts_config-periodic-flush

手元の環境ではフラッシュするのを1MBごとにして様子見をしている。10MBで試したときはあっさりread onlyになった。

"c:\Program Files\Oracle\VirtualBox\VBoxManage.exe" setextradata "ubuntu1204up-amd64" "VBoxInternal/Devices/ahci/0/LUN0/Config/FlushInterval" 1000000 

SylpheedでHTMLメールと添付ありのメールを区別する

とつぶやいている人がいて、なるほどと思ったので、やってみました。

  • text/plain,text/htmlのみからなるメールについては小さなメールアイコンを表示
  • それ以外は従来通りクリップアイコンを表示

という動作をするようにしてみました。


上記を実現するためのパッチは以下の通りです。

diff -uNr '--exclude=.svn' sylpheed.orig/src/icons/htmlmail.xpm sylpheed.rev/src/icons/htmlmail.xpm
--- sylpheed.orig/src/icons/htmlmail.xpm	1970-01-01 09:00:00.000000000 +0900
+++ sylpheed.rev/src/icons/htmlmail.xpm	2012-08-18 16:29:42.000000000 +0900
@@ -0,0 +1,44 @@
+/* XPM */
+static char *htmlmail_xpm[] = {
+"8 12 28 1",
+" 	c None",
+"B	c #000000",
+"C	c #474747",
+"D	c #F5F5F5",
+"E	c #323232",
+"F	c #F4F4F4",
+"G	c #C1C1C1",
+"H	c #EAEAEA",
+"I	c #EBEBEB",
+"J	c #AAAAAA",
+"K	c #3D3D3D",
+"L	c #EFEFEF",
+"M	c #F3F3F3",
+"N	c #3F3F3F",
+"O	c #464646",
+"P	c #BDBDBD",
+"Q	c #D6D6D6",
+"R	c #A0A0A0",
+"S	c #F2F2F2",
+"T	c #BABABA",
+"U	c #E9E9E9",
+"V	c #8C8C8C",
+"W	c #BFBFBF",
+"X	c #DADADA",
+"Y	c #CDCDCD",
+"Z	c #A7A7A7",
+"a	c #D3D3D3",
+"b	c #282828",
+"        ",
+"        ",
+"        ",
+" BBBBBB ",
+"BBDDDDBB",
+"BDCLDCDB",
+"BDDCCDDB",
+"BDCGGCDB",
+"BCDDDDCB",
+" BBBBBB ",
+"        ",
+"        "
+};
diff -uNr '--exclude=.svn' sylpheed.orig/src/stock_pixmap.c sylpheed.rev/src/stock_pixmap.c
--- sylpheed.orig/src/stock_pixmap.c	2012-08-18 16:28:20.000000000 +0900
+++ sylpheed.rev/src/stock_pixmap.c	2012-08-18 16:29:20.000000000 +0900
@@ -34,6 +34,7 @@
 #include "icons/deleted.xpm"
 #include "icons/error.xpm"
 #include "icons/forwarded.xpm"
+#include "icons/htmlmail.xpm"
 #include "icons/interface.xpm"
 #include "icons/jpilot.xpm"
 #include "icons/ldap.xpm"
@@ -110,6 +111,7 @@
 	{error_xpm	 , NULL, NULL},
 	{forwarded_xpm	 , NULL, NULL},
 	{NULL, NULL, NULL, NULL, group, sizeof(group), "group", 0},
+	{htmlmail_xpm	 , NULL, NULL},
 	{interface_xpm	 , NULL, NULL},
 	{jpilot_xpm	 , NULL, NULL},
 	{ldap_xpm	 , NULL, NULL},
diff -uNr '--exclude=.svn' sylpheed.orig/src/stock_pixmap.h sylpheed.rev/src/stock_pixmap.h
--- sylpheed.orig/src/stock_pixmap.h	2012-08-18 16:28:20.000000000 +0900
+++ sylpheed.rev/src/stock_pixmap.h	2012-08-18 16:29:20.000000000 +0900
@@ -38,6 +38,7 @@
 	STOCK_PIXMAP_ERROR,
 	STOCK_PIXMAP_FORWARDED,
 	STOCK_PIXMAP_GROUP,
+	STOCK_PIXMAP_HTMLMAIL,
 	STOCK_PIXMAP_INTERFACE,
 	STOCK_PIXMAP_JPILOT,
 	STOCK_PIXMAP_LDAP,
diff -uNr '--exclude=.svn' sylpheed.orig/src/summaryview.c sylpheed.rev/src/summaryview.c
--- sylpheed.orig/src/summaryview.c	2012-08-18 16:28:20.000000000 +0900
+++ sylpheed.rev/src/summaryview.c	2012-08-18 16:38:22.000000000 +0900
@@ -139,6 +139,7 @@
 static GdkPixbuf *forwarded_pixbuf;
 
 static GdkPixbuf *clip_pixbuf;
+static GdkPixbuf *htmlmail_pixbuf;
 
 static void summary_clear_list_full	(SummaryView		*summaryview,
 					 gboolean		 is_refresh);
@@ -624,6 +625,8 @@
 			 &forwarded_pixbuf);
 	stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_CLIP,
 			 &clip_pixbuf);
+	stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_HTMLMAIL,
+			 &htmlmail_pixbuf);
 
 	font_desc = pango_font_description_new();
 	size = pango_font_description_get_size
@@ -2416,6 +2419,7 @@
 	MsgFlags flags;
 	GdkColor color;
 	gint color_val;
+	MimeInfo *mimeinfo = NULL;
 
 	if (!msginfo) {
 		GET_MSG_INFO(msginfo, iter);
@@ -2473,8 +2477,23 @@
 	else if (MSG_IS_FORWARDED(flags))
 		unread_pix = forwarded_pixbuf;
 
-	if (MSG_IS_MIME(flags))
-		mime_pix = clip_pixbuf;
+	if (MSG_IS_MIME(flags)) {
+		mimeinfo = procmime_scan_message(msginfo);
+		while (mimeinfo) {
+			if (mimeinfo->mime_type != MIME_TEXT &&
+			    mimeinfo->mime_type != MIME_TEXT_HTML &&
+			    mimeinfo->mime_type != MIME_MULTIPART) {
+				break;
+			} else {
+				mimeinfo = procmime_mimeinfo_next(mimeinfo);
+			}
+		}
+		if (mimeinfo) {
+			mime_pix = clip_pixbuf;
+		} else {
+			mime_pix = htmlmail_pixbuf;
+		}
+	}
 
 	if (prefs_common.bold_unread) {
 		if (MSG_IS_UNREAD(flags))

Sylpheed でHTMLメールを閲覧できるようにする

Windowsでも頑張ってみた続報はこちら。id:kenhys:20130218
Sylpheed でHTMLメールを閲覧するとテキストで表示されます。
リンクとかは有効なのですが、ちょっと残念な感じです。


Sylpheedにはプラグインを追加できますね!
というわけで、HTMLメールを描画するためのプラグインを作ってみました。


丁度良いHTMLメールのサンプルがなかったのでアレですがメッセージビューに
Googleロゴ画像がきちんとレンダリングされています。

HTMLのレンダリングにはWebKitGTK+を使ってみました。


ソースコードは以下から入手してコンパイルしてください。
Ubuntuだとapt-get install libwebkitgtk-devをあらかじめインストールしておく必要があります。

https://github.com/kenhys/sylpheed-htmlview

tar.gzが欲しい人は以下のリンクから入手できます。
https://github.com/kenhys/sylpheed-htmlview/tarball/0.1.0


Windows用にバイナリを用意したいところだけども、
WebKitGTK+のコンパイル済みバイナリって(MinGW用)って公式サイトでは提供していないのか。うーむ。

growl for linuxを使ってみた

Ubuntu環境で作業をすることが増えました。
通知をポップアップで出したいときはどうするのかと調べてみるとnotify-sendコマンドを使うととてもお手軽なようです。

ただ、notify-sendで表示を継続する時間の指定が効いていないようにみえます。
このへんが関係ありそうです。
https://bugs.launchpad.net/ubuntu/+source/notify-osd/+bug/390508


代替を探してみるとgrowl for linuxというのがありました。
http://mattn.kaoriya.net/software/growl4linux.htm

時間指定はおよそ5秒固定でしたが、見た目のスタイルを選択できたり、
連続した通知が重ならずに表示される点はnotify-sendより良さげです。
ちなみに、見た目のスタイルはfog(これがデフォルト),balloon,nico2などを選択できます。
(以下の参考画像はgithubへのリンクです。)

fog
https://github.com/mattn/growl-for-linux/raw/master/data/display_default.png

balloon
https://github.com/mattn/growl-for-linux/raw/master/data/display_balloon.png

nico2
https://github.com/mattn/growl-for-linux/raw/master/data/display_nico2.png

ちなみに、growl for linuxでの見た目の変更は~/.config/gol/config.dbを書き換えることで行います。
configテーブルのdefault_displayを適当な値に変更すれば良いです。

-[3305]% sqlite3 ~/.config/gol/config.db
SQLite version 3.7.9 2011-11-01 00:52:41
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from config;
version|0.6.7
default_display|fog
default_timeout|3000
sqlite> update config set value="balloon" where key="default_display";

表示時間の秒数が固定になっている件については、パッチを書いてみました。
通知ごとに表示時間を指定できる、というのではなくてデフォルトの表示時間をカスタマイズできるようにするものです。
https://github.com/mattn/growl-for-linux/pull/23


追記:
開発者の方からのコメントにあるように以下のようにしてwhitelistに設定してやると通知バーから設定画面を表示させることができました。
気づいてなかった。

gsettings set com.canonical.Unity.Panel systray-whitelist "$(gsettings get com.canonical.Unity.Panel systray-whitelist | sed -e "s/]$/, 'growl-for-linux']/")"