Gaim 中打开中文 URI

[Gaim] 上 IRC, 在 #debian-zh@freenode.net 看到了一个[url=http://zh.wikipedia.org/wiki/标点符号][中文链接][/url], 然后就右键点击这个链接, 选择 "Open Link in Brower", 那么不管在 Firefox 还是 IE 中都显示的是 "鏍囩偣绗﹀彿", 当然在 wikipedia 上是找不到了的. 第一感觉就是字符个数不对, 原来是四个, 现在是六个, 估计跟 UTF-8 有关系. 在 Gaim 中使用的是 UTF-8, 每个中文字是三个字节, GBK/GB2312 每个中文是两个字节, 这样字节数就对上了.

首先需要在 Firefox 中地址栏输入"about:config", filter 中输入 "url", 然后双击"network.standard-url.encode-utf8" 把缺省的 false 改成 true, 这样在命令行中 "firefox.exe http://zh.wikipedia.org/wiki/标点符号" 以及在地址栏中直接输入带有中文的 URL 也没有问题的.

然后在 Emacs 中做测试, "标点符号"的 UTF-8 编码是 E6A087 E782B9 E7ACA6 E58FB7, 每两个字节拆开, 在 hexl-mode 下编辑一个文件, 就成了 E6A0 87E7 82B9 E7AC A6E5 8FB7, 以 RAW 保存, 用其他文本工具打开(比如 notepad), 显示的就是 "鏍囩偣绗﹀彿". 说明猜想是正确的.

那么原因是什么呢? 在 Gaim 中, 当点击 "Open Link in Brower" 时候, 调用过程如下:

  1. src/gtkimhtml.c:1567: tag_event()
  2. src/gtkimhtml.c:1488: url_open() /* 触发了一个 signal URL_CLICKED "url_clicked" */
  3.  
  4. src/gtkutils.c:95: gaim_setup_imhtml() /* 连接到 signal "url_clicked" */
  5. src/gtkutils.c:77: url_clicked_cb()
  6. src/gtkutils.c:69: url_clicked_idle_cb()
  7. src/notify.c:401: gaim_notify_uri()
  8. src/gtknotify.c:1084: GaimNotifyUiOps ops; /* 定义了 notify_uri 为 gaim_gtk_notify_uri() */
  9. src/gtknotify.c:1069: gaim_gtk_notify_uri()
  10. src/w32def.c:361: wgaim_notify_uri() /* 最后调用了 ShellExecuteEx */

先看看非 Windows 系统上是怎么处理的, 同样是在 gaim_gtk_notify_uri() 中, 首先就对 URI 进行了转义处理, 也就是转成了 "%E6%A0%87%E7%82%B9%E7%AC%A6%E5%8F%B7", 这样直接用 Firefox, Opera 或者其他浏览器是没有问题的.

对于 Windows 来讲, 对于一个 URI 直接调用 ShellExecuteEx 需要在注册表中查找此协议的处理命令, 为 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\HTTP\shell\open\command, 如果设置 Firefox 为缺省浏览器的话, 此值为 FIREFOX.EXE -url "%1". 这样就把 UTF-8 编码的 URI 直接传递给了 Firefox. 至于 Firefox 里面怎么处理, 没看代码不太清楚, 但是在命令行直接输入却没有问题, 一下是猜想, 说明里面有一个转换(GBK/GB2312->UTF-8), 对于 UTF-8 编码的 URI, 把它当作了 GBK/GB2312 编码, 然后要转换成 UTF-8, 发到服务器以后显示出来就是我们所看到的"鏍囩偣绗﹀彿".

此想法可以通过编辑一个批处理文件来验证, 以下文件以 UTF-8 保存为 test.bat:

  1. firefox.exe http://zh.wikipedia.org/wiki/标点符号

运行 test.bat 可以看到 cmd 的窗口中显示的就是类似的乱码, 也更确定了是 UTF-8 编码被当作成了 GBK/GB2312.

但是问题该怎么解决呢? 最好的办法是在 Gaim 的 wgaim_notify_uri() 之前仍然进行转义, 这样在任何情况下都不会出错.

RFC 1738

RFC 1738: Uniform Resource Locators (URL) 描述了 URL 具体应该是什么样.

问题解决

参照了 [Sylpheed]utils.c : execute_open_file, 对 win32dep.c : wgaim_notify_uri 进行了同样的改写, 如下:

  1. Index: win32/win32dep.c
  2.  
  3. ===================================================================
  4.  
  5. --- win32/win32dep.c    (revision 16186)
  6.  
  7. +++ win32/win32dep.c    (working copy)
  8.  
  9. @@ -41,6 +41,8 @@
  10.  
  11.  #include "zlib.h"
  12.  #include "untar.h"
  13.  
  14. +#include "util.h"
  15. +
  16.  #include <libintl.h>
  17.  
  18.  #include "win32dep.h"
  19. @@ -359,22 +361,52 @@
  20.  
  21.  }
  22.  
  23.  void wgaim_notify_uri(const char *uri) {
  24. -       SHELLEXECUTEINFO sinfo;
  25.  
  26. -       memset(&sinfo, 0, sizeof(sinfo));
  27. -       sinfo.cbSize = sizeof(sinfo);
  28. -       sinfo.fMask = SEE_MASK_CLASSNAME;
  29. -       sinfo.lpVerb = "open";
  30. -       sinfo.lpFile = uri;
  31. -       sinfo.nShow = SW_SHOWNORMAL;
  32. -       sinfo.lpClass = "http";
  33. +        if (G_WIN32_HAVE_WIDECHAR_API()) {
  34. +            SHELLEXECUTEINFOW wsinfo;
  35. +            wchar_t *w_uri;
  36.  
  37. -       /* We'll allow whatever URI schemes are supported by the
  38. -          default http browser.
  39. -       */
  40. -       if(!ShellExecuteEx(&sinfo))
  41. +            w_uri = g_utf8_to_utf16(uri, -1, NULL, NULL, NULL);
  42. +
  43. +            memset(&wsinfo, 0, sizeof(wsinfo));
  44. +            wsinfo.cbSize = sizeof(wsinfo);
  45. +            wsinfo.fMask = SEE_MASK_CLASSNAME;
  46. +            wsinfo.lpVerb = L"open";
  47. +            wsinfo.lpFile = w_uri;
  48. +            wsinfo.nShow = SW_SHOWNORMAL;
  49. +            wsinfo.lpClass = L"http";
  50. +
  51. +            gaim_debug(GAIM_DEBUG_INFO, "wgaim_notify_uri", "The wide uri is %s\n", uri);
  52. +            if(!ShellExecuteExW(&wsinfo))
  53.                 gaim_debug_error("wgaim", "Error opening URI: %s error: %d\n",
  54. -                       uri, (int) sinfo.hInstApp);
  55. +                                 uri, (int) wsinfo.hInstApp);
  56. +
  57. +            g_free(w_uri);
  58. +        } else {
  59. +
  60. +            SHELLEXECUTEINFO sinfo;
  61. +            gchar *locale_uri;
  62. +
  63. +            locale_uri = g_locale_from_utf8(uri, -1, NULL, NULL, NULL);
  64. +
  65. +            memset(&sinfo, 0, sizeof(sinfo));
  66. +            sinfo.cbSize = sizeof(sinfo);
  67. +            sinfo.fMask = SEE_MASK_CLASSNAME;
  68. +            sinfo.lpVerb = "open";
  69. +            sinfo.lpFile = locale_uri;
  70. +            sinfo.nShow = SW_SHOWNORMAL;
  71. +            sinfo.lpClass = "http";
  72. +
  73. +            /* We'll allow whatever URI schemes are supported by the
  74. +               default http browser.
  75. +            */
  76. +            gaim_debug(GAIM_DEBUG_INFO, "wgaim_notify_uri", "The locale uri is %s\n", uri);
  77. +            if(!ShellExecuteEx(&sinfo))
  78. +               gaim_debug_error("wgaim", "Error opening URI: %s error: %d\n",
  79. +                                 uri, (int) sinfo.hInstApp);
  80. +
  81. +            g_free(locale_uri);
  82. +        }
  83.  }
  84.  
  85.  void wgaim_init(HINSTANCE hint) {

已经在 [Gaim Tracker] 上提交了 patch, 看有没有人处理, 在我这里没什么问题. 我估计在 Linux 下, 非 UTF-8 的 locale 下也仍然有这个问题, 可惜没有测试环境来测试一下.

已经提交

此 Patch 在昨天晚上由 Daniel 'datallah' Atallah 提交.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • You can use BBCode tags in the text.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
  _____    ____                              __  __        
|___ / / ___| ___ __ __ _ __ _ __ \ \/ / __ _
|_ \ | | / __| \ \/ / | '__| | '_ \ \ / / _` |
___) | | |___ \__ \ > < | | | |_) | / \ | (_| |
|____/ \____| |___/ /_/\_\ |_| | .__/ /_/\_\ \__,_|
|_|
Enter the code depicted in ASCII art style.