<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>似水年華</title><link href="https://chyen.cc/blog/" rel="alternate"/><link href="https://chyen.cc/blog/feeds/all.atom.xml" rel="self"/><id>https://chyen.cc/blog/</id><updated>2024-08-22T23:45:00+08:00</updated><entry><title>搶救「統一發票兌獎」APP 資料</title><link href="https://chyen.cc/blog/posts/2024/08/22/invoices-app-data-recovery.html" rel="alternate"/><published>2024-08-22T23:45:00+08:00</published><updated>2024-08-22T23:45:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2024-08-22:/blog/posts/2024/08/22/invoices-app-data-recovery.html</id><summary type="html">&lt;p&gt;臺灣財政部出品的 &lt;a href="https://play.google.com/store/apps/details?id=tw.gov.invoice"&gt;統一發票兌獎&lt;/a&gt; 近期更新後，需要重新登入，且手動掃描/輸入的發票，與超過一定期限的電子發票資料皆 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;臺灣財政部出品的 &lt;a href="https://play.google.com/store/apps/details?id=tw.gov.invoice"&gt;統一發票兌獎&lt;/a&gt; 近期更新後，需要重新登入，且手動掃描/輸入的發票，與超過一定期限的電子發票資料皆消失。Google Play 的部份相關評論下有以下回覆：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;您好，因Google官方授權通訊協定(OAuth)政策異動，原兌獎APP之安卓裝置將無法進行備份，故新版本配合調整相關協定以符合Google政策要求，致安卓手機於更版後會有自動登出現象。有關會員卡及掃描/手動輸入發票，倘有自行「登出選保留」或有進行「資料備份」者皆能保有資料，若無進行上述操作則資料將無法還原，造成不便，尚祈見諒。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根據我的分析，原有數據都還在，只是新版 APP 不會去使用，因此一個可能的解法是使用 &lt;code&gt;adb install -d&lt;/code&gt; 降級回舊版 APP，再將資料備份。然而，較久遠以前的電子發票仍無法存取。對此，若已取得 Android 系統的 root 權限，則可手動將舊版 APP 資料轉換為新版格式，以還原所有資料。&lt;/p&gt;
&lt;h1&gt;Data migration steps&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Close and force stop the app to avoid data corruption during migration&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get the old database&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-iv&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/InvoiceApp.db3&lt;span class="w"&gt; &lt;/span&gt;/sdcard/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find out the old database key&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/uuidFile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the old database in &lt;a href="https://sqlitebrowser.org/"&gt;SQLite Browser&lt;/a&gt;. Here the old database is encrypted with the key found in the previous step using SQLCipher 3 defaults.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Open the old encrypted database" src="https://chyen.cc/blog/images/open-encrypted-database.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find out the new database key&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/uuidFile.uuid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Migrate the database to use the new key and SQLCipher 4 defaults. It can be done in SQLite Browser via Tools -&amp;gt; Set Encryption. Note that after this step SQLite Browser will reload the database, which now needs the new key and new defaults.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Set encryption parameters to match the new database" src="https://chyen.cc/blog/images/set-encryption.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make a backup of the database at the new path&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mv&lt;span class="w"&gt; &lt;/span&gt;-iv&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/InvoiceApp.db3&lt;span class="o"&gt;{&lt;/span&gt;,.bak&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the migrated database to the new path&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-iv&lt;span class="w"&gt; &lt;/span&gt;/sdcard/InvoiceApp.db3&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/InvoiceApp.db3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find out owner/group and SELlinux context for application data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/system/bin/ls&lt;span class="w"&gt; &lt;/span&gt;-alZ&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update owner/group and SELinux context of the copied file to match existing files. Note that here &lt;code&gt;u0_a261:u0_a261&lt;/code&gt; and &lt;code&gt;u:object_r:app_data_file:s0:c5,c257,c512,c768&lt;/code&gt; are just examples. Actual values should be checked from the previous step. More details can be found in &lt;a href="https://chyen.cc/blog/posts/2021/03/15/migrate-android-app-data.html"&gt;this article&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;chown&lt;span class="w"&gt; &lt;/span&gt;u0_a261:u0_a261&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/InvoiceApp.db3
chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/InvoiceApp.db3
/system/bin/chcon&lt;span class="w"&gt; &lt;/span&gt;u:object_r:app_data_file:s0:c5,c257,c512,c768&lt;span class="w"&gt; &lt;/span&gt;/data/data/tw.gov.invoice/files/.config/InvoiceApp.db3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;</content><category term="misc"/></entry><entry><title>Linux 與 Windows 下顯示在螢幕最上層的時鐘</title><link href="https://chyen.cc/blog/posts/2021/07/13/always-on-top-clock.html" rel="alternate"/><published>2021-07-13T00:00:00+08:00</published><updated>2021-07-13T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2021-07-13:/blog/posts/2021/07/13/always-on-top-clock.html</id><summary type="html">&lt;p&gt;最近跟老師開會，常常討論太久，超過老師應該要接小孩的時間。要怎麼避免類似的情況再發生呢？我覺得可以在用投影片 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;最近跟老師開會，常常討論太久，超過老師應該要接小孩的時間。要怎麼避免類似的情況再發生呢？我覺得可以在用投影片報告的時候，螢幕上放一個顯眼的時鐘。&lt;/p&gt;
&lt;p&gt;在 Linux 上，我平常使用的 &lt;a href="https://www.nongnu.org/devilspie2/"&gt;devilspie2&lt;/a&gt; 能夠讓特定的視窗維持最上層顯示。例如，使用 xclock 時，可以在 &lt;code&gt;$XDG_CONFIG_HOME/devilspie2/devilspie2.lua&lt;/code&gt; 加入以下內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;window_class_lower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xclock&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;make_always_on_top&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kr"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;這邊 window class 可以用 &lt;code&gt;xprop&lt;/code&gt; 指令確認。設定好之後，xclock 就能顯示在 LibreOffice 上了。&lt;/p&gt;
&lt;p&gt;而在 Windows 上，我找了幾個時鐘程式，都沒辦法顯示在 PowerPoint 投影片放映時顯示在最上層。後來有人提到對時工具 NTPClock 有這個功能。一試之下，終於成功！&lt;/p&gt;
&lt;p&gt;&lt;img alt="NTPClock 中的「桌面最上層」選項" src="https://chyen.cc/blog/images/ntpclock-always-on-top.png"&gt;&lt;/p&gt;
&lt;h4&gt;2022-12-23 update&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;make_always_on_top()&lt;/code&gt; uses &lt;code&gt;_NET_WM_STATE_ABOVE&lt;/code&gt; &lt;a href="https://github.com/dsalt/devilspie2/blob/v0.44/src/script_functions.c#L415"&gt;1&lt;/a&gt;&lt;a href="https://gitlab.gnome.org/GNOME/libwnck/-/blob/43.0/libwnck/window.c?ref_type=tags#L1668"&gt;2&lt;/a&gt;, so it needs &lt;a href="https://gitlab.xfce.org/xfce/xfwm4/-/issues/686"&gt;xfwm4 &amp;gt;= 4.18&lt;/a&gt; or another standard-violating window manager to make xclock appear above libreoffice slideshow.&lt;/p&gt;</content><category term="Work"/></entry><entry><title>一窺內容農場SEO技巧</title><link href="https://chyen.cc/blog/posts/2021/06/03/content-farm-connections.html" rel="alternate"/><published>2021-06-03T00:00:00+08:00</published><updated>2021-06-03T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2021-06-03:/blog/posts/2021/06/03/content-farm-connections.html</id><summary type="html">&lt;h1&gt;緣起&lt;/h1&gt;
&lt;p&gt;內容農場猖獗已久，近日尤甚。
加上各個內容農場善用 SEO 技巧，使得使用 Google 搜尋時，不時能見到內容農場的文章。
雖有 &lt;a href="https://github.com/danny0838/content-farm-terminator"&gt;終 …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;h1&gt;緣起&lt;/h1&gt;
&lt;p&gt;內容農場猖獗已久，近日尤甚。
加上各個內容農場善用 SEO 技巧，使得使用 Google 搜尋時，不時能見到內容農場的文章。
雖有 &lt;a href="https://github.com/danny0838/content-farm-terminator"&gt;終結內容農場&lt;/a&gt; 等瀏覽器外掛，以及能搭配 &lt;a href="https://github.com/iorate/ublacklist"&gt;uBlacklist&lt;/a&gt; 使用的 &lt;a href="https://github.com/cobaltdisco/Google-Chinese-Results-Blocklist"&gt;Google-Chinese-Results-Blocklist&lt;/a&gt; 等清單，終究是治標不治本。
而近日在替 Google-Chinese-Results-Blocklist 編修清單時，發現了一個內容農場使用的 SEO 小技巧。
聊表分享，以資雜談。&lt;/p&gt;
&lt;h1&gt;探究&lt;/h1&gt;
&lt;p&gt;luoow, jishuwen, weiwenku, mdeditor, gushiciku 等站都是我手動維護的一份內容農場清單中的項目。
這幾個網域之間具有互相 HTTP 3xx 跳轉的關係。
藉由Wayback Machine，可以看出網域的變遷。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jishuwen: 2020年九月底、十月初之間，由&lt;a href="https://web.archive.org/web/2020*/https://www.jishuwen.com/d/psBI"&gt;自行收錄文章&lt;/a&gt;，改為&lt;a href="https://web.archive.org/web/2020*/https://www.jishuwen.com/d/pYBD/zh-tw"&gt;跳轉到 mdeditor&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;luoow: 2021年五月上旬，由&lt;a href="https://web.archive.org/web/*/https://www.luoow.com/dc_hk/104791373"&gt;自行收錄文章&lt;/a&gt;改為&lt;a href="https://web.archive.org/web/*/https://www.luoow.com/dc_tw/110023596"&gt;跳轉到 gushiciku&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;mdeditor: 2021年五月上旬，由&lt;a href="https://web.archive.org/web/*/https://www.mdeditor.tw/pl/pcr2/zh-tw"&gt;自行收錄文章&lt;/a&gt;，改為&lt;a href="https://web.archive.org/web/*/https://www.mdeditor.tw/pl/gvK3/zh-tw"&gt;跳轉到 gushiciku&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;weiwenku: 先前是自行收錄文章，2021年四月底開始有&lt;a href="https://web.archive.org/web/*/https://www.weiwenku.org/d/107438579"&gt;跳轉到 luoow&lt;/a&gt; 的紀錄。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而藉由 WHOIS 資料，也可以一窺各個網域之間的連繫。
其中一個網域註冊人為吴阳平，另一個網域的註冊人為 yangping wu。搜索這個名字會出現&lt;a href="https://www.facebook.com/yangping.wu.148"&gt;此 Facebook 帳號&lt;/a&gt;，其中註明了此人與其中一個網域微文庫的關聯，似乎是同一個人。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
gushiciku WHOIS 資料
&lt;/summary&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois gushiciku.cn
Domain Name: gushiciku.cn
ROID: 20190517s10001s12057304-cn
Domain Status: ok
Registrant: 吴阳平
Registrant Contact Email: 185159865@qq.com
Sponsoring Registrar: 阿里云计算有限公司（万网）
Name Server: jill.ns.cloudflare.com
Name Server: dave.ns.cloudflare.com
Registration Time: 2019-05-17 10:45:36
Expiration Time: 2022-05-17 10:45:36
DNSSEC: unsigned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;
mdeditor WHOIS 資料
&lt;/summary&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois mdeditor.tw
Domain Name: mdeditor.tw
   Domain Status: ok
   Registrant:
      yangping wu  896500373@qq.com

   Administrative Contact:
      yangping wu  896500373@qq.com

   Technical Contact:
      yangping wu  896500373@qq.com

   Record expires on 2023-05-14 17:09:45 (UTC+8)
   Record created on 2020-05-14 17:09:45 (UTC+8)

   Domain servers in listed order:
      dave.ns.cloudflare.com
      jill.ns.cloudflare.com

Registration Service Provider: GoDaddy
Registration Service URL: http://www.GoDaddy.com/

Provided by NeuStar Registry Gateway Services
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;
luoow WHOIS 資料
&lt;/summary&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois luoow.com
   Domain Name: LUOOW.COM
   Registry Domain ID: 2419015362_DOMAIN_COM-VRSN
   Registrar WHOIS Server: grs-whois.hichina.com
   Registrar URL: http://www.net.cn
   Updated Date: 2020-12-19T05:23:00Z
   Creation Date: 2019-08-01T10:34:54Z
   Registry Expiry Date: 2021-08-01T10:34:54Z
   Registrar: Alibaba Cloud Computing (Beijing) Co., Ltd.
   Registrar IANA ID: 420
   Registrar Abuse Contact Email: DomainAbuse@service.aliyun.com
   Registrar Abuse Contact Phone: +86.95187
   Domain Status: ok https://icann.org/epp#ok
   Name Server: DAVE.NS.CLOUDFLARE.COM
   Name Server: JILL.NS.CLOUDFLARE.COM
   DNSSEC: unsigned
   URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
&amp;gt;&amp;gt;&amp;gt; Last update of whois database: 2021-06-03T16:00:00Z &amp;lt;&amp;lt;&amp;lt;

For more information on Whois status codes, please visit https://icann.org/epp

NOTICE: The expiration date displayed in this record is the date the
registrar&amp;#39;s sponsorship of the domain name registration in the registry is
currently set to expire. This date does not necessarily reflect the expiration
date of the domain name registrant&amp;#39;s agreement with the sponsoring
registrar.  Users may consult the sponsoring registrar&amp;#39;s Whois database to
view the registrar&amp;#39;s reported date of expiration for this registration.

TERMS OF USE: You are not authorized to access or query our Whois
database through the use of electronic processes that are high-volume and
automated except as reasonably necessary to register domain names or
modify existing registrations; the Data in VeriSign Global Registry
Services&amp;#39; (&amp;quot;VeriSign&amp;quot;) Whois database is provided by VeriSign for
information purposes only, and to assist persons in obtaining information
about or related to a domain name registration record. VeriSign does not
guarantee its accuracy. By submitting a Whois query, you agree to abide
by the following terms of use: You agree that you may use this Data only
for lawful purposes and that under no circumstances will you use this Data
to: (1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail, telephone,
or facsimile; or (2) enable high volume, automated, electronic processes
that apply to VeriSign (or its computer systems). The compilation,
repackaging, dissemination or other use of this Data is expressly
prohibited without the prior written consent of VeriSign. You agree not to
use electronic processes that are automated and high-volume to access or
query the Whois database except as reasonably necessary to register
domain names or modify existing registrations. VeriSign reserves the right
to restrict your access to the Whois database in its sole discretion to ensure
operational stability.  VeriSign may restrict or terminate your access to the
Whois database for failure to abide by these terms of use. VeriSign
reserves the right to modify these terms at any time.

The Registry database contains ONLY .COM, .NET, .EDU domains and
Registrars.
Domain Name: luoow.com
Registry Domain ID: 2419015362_DOMAIN_COM-VRSN
Registrar WHOIS Server: grs-whois.hichina.com
Registrar URL: http://whois.aliyun.com
Updated Date: 2020-07-08T12:42:47Z
Creation Date: 2019-08-01T10:34:54Z
Registrar Registration Expiration Date: 2021-08-01T10:34:54Z
Registrar: Alibaba Cloud Computing (Beijing) Co., Ltd.
Registrar IANA ID: 420
Reseller:
Domain Status: ok https://icann.org/epp#ok
Registrant City:
Registrant State/Province: Hu Nan
Registrant Country: CN
Registrant Email:https://whois.aliyun.com/whois/whoisForm
Registry Registrant ID: Not Available From Registry
Name Server: DNS11.HICHINA.COM
Name Server: DNS12.HICHINA.COM
DNSSEC: unsigned
Registrar Abuse Contact Email: DomainAbuse@service.aliyun.com
Registrar Abuse Contact Phone: +86.95187
URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/
&amp;gt;&amp;gt;&amp;gt;Last update of WHOIS database: 2021-06-03T16:00:22Z &amp;lt;&amp;lt;&amp;lt;

For more information on Whois status codes, please visit https://icann.org/epp

Important Reminder: Per ICANN 2013RAA`s request, Hichina has modified domain names`whois format of dot com/net/cc/tv, you could refer to section 1.4 posted by ICANN on http://www.icann.org/en/resources/registrars/raa/approved-with-specs-27jun13-en.htm#whois The data in this whois database is provided to you for information purposes only, that is, to assist you in obtaining information about or related to a domain name registration record. We make this information available &amp;quot;as is,&amp;quot; and do not guarantee its accuracy. By submitting a whois query, you agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to: (1)enable high volume, automated, electronic processes that stress or load this whois database system providing you this information; or (2) allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations via direct mail, electronic mail, or by telephone.  The compilation, repackaging, dissemination or other use of this data is expressly prohibited without prior written consent from us. We reserve the right to modify these terms at any time. By submitting this query, you agree to abide by these terms.For complete domain details go to:http://whois.aliyun.com/whois/domain/hichina.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;details&gt;
&lt;summary&gt;
gushiciku WHOIS 資料
&lt;/summary&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois jishuwen.com
   Domain Name: JISHUWEN.COM
   Registry Domain ID: 2313252870_DOMAIN_COM-VRSN
   Registrar WHOIS Server: grs-whois.hichina.com
   Registrar URL: http://www.net.cn
   Updated Date: 2020-09-17T01:35:51Z
   Creation Date: 2018-09-23T14:25:57Z
   Registry Expiry Date: 2021-09-23T14:25:57Z
   Registrar: Alibaba Cloud Computing (Beijing) Co., Ltd.
   Registrar IANA ID: 420
   Registrar Abuse Contact Email: DomainAbuse@service.aliyun.com
   Registrar Abuse Contact Phone: +86.95187
   Domain Status: ok https://icann.org/epp#ok
   Name Server: DAVE.NS.CLOUDFLARE.COM
   Name Server: JILL.NS.CLOUDFLARE.COM
   DNSSEC: unsigned
   URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
&amp;gt;&amp;gt;&amp;gt; Last update of whois database: 2021-06-03T16:02:35Z &amp;lt;&amp;lt;&amp;lt;

For more information on Whois status codes, please visit https://icann.org/epp

NOTICE: The expiration date displayed in this record is the date the
registrar&amp;#39;s sponsorship of the domain name registration in the registry is
currently set to expire. This date does not necessarily reflect the expiration
date of the domain name registrant&amp;#39;s agreement with the sponsoring
registrar.  Users may consult the sponsoring registrar&amp;#39;s Whois database to
view the registrar&amp;#39;s reported date of expiration for this registration.

TERMS OF USE: You are not authorized to access or query our Whois
database through the use of electronic processes that are high-volume and
automated except as reasonably necessary to register domain names or
modify existing registrations; the Data in VeriSign Global Registry
Services&amp;#39; (&amp;quot;VeriSign&amp;quot;) Whois database is provided by VeriSign for
information purposes only, and to assist persons in obtaining information
about or related to a domain name registration record. VeriSign does not
guarantee its accuracy. By submitting a Whois query, you agree to abide
by the following terms of use: You agree that you may use this Data only
for lawful purposes and that under no circumstances will you use this Data
to: (1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail, telephone,
or facsimile; or (2) enable high volume, automated, electronic processes
that apply to VeriSign (or its computer systems). The compilation,
repackaging, dissemination or other use of this Data is expressly
prohibited without the prior written consent of VeriSign. You agree not to
use electronic processes that are automated and high-volume to access or
query the Whois database except as reasonably necessary to register
domain names or modify existing registrations. VeriSign reserves the right
to restrict your access to the Whois database in its sole discretion to ensure
operational stability.  VeriSign may restrict or terminate your access to the
Whois database for failure to abide by these terms of use. VeriSign
reserves the right to modify these terms at any time.

The Registry database contains ONLY .COM, .NET, .EDU domains and
Registrars.
Domain Name: jishuwen.com
Registry Domain ID: 2313252870_DOMAIN_COM-VRSN
Registrar WHOIS Server: grs-whois.hichina.com
Registrar URL: http://whois.aliyun.com
Updated Date: 2019-09-17T05:56:35Z
Creation Date: 2018-09-23T14:25:57Z
Registrar Registration Expiration Date: 2021-09-23T14:25:57Z
Registrar: Alibaba Cloud Computing (Beijing) Co., Ltd.
Registrar IANA ID: 420
Reseller:
Domain Status: ok https://icann.org/epp#ok
Registrant City:
Registrant State/Province: Hu Nan
Registrant Country: CN
Registrant Email:https://whois.aliyun.com/whois/whoisForm
Registry Registrant ID: Not Available From Registry
Name Server: DAVE.NS.CLOUDFLARE.COM
Name Server: JILL.NS.CLOUDFLARE.COM
DNSSEC: unsigned
Registrar Abuse Contact Email: DomainAbuse@service.aliyun.com
Registrar Abuse Contact Phone: +86.95187
URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/
&amp;gt;&amp;gt;&amp;gt;Last update of WHOIS database: 2021-06-03T16:02:53Z &amp;lt;&amp;lt;&amp;lt;

For more information on Whois status codes, please visit https://icann.org/epp

Important Reminder: Per ICANN 2013RAA`s request, Hichina has modified domain names`whois format of dot com/net/cc/tv, you could refer to section 1.4 posted by ICANN on http://www.icann.org/en/resources/registrars/raa/approved-with-specs-27jun13-en.htm#whois The data in this whois database is provided to you for information purposes only, that is, to assist you in obtaining information about or related to a domain name registration record. We make this information available &amp;quot;as is,&amp;quot; and do not guarantee its accuracy. By submitting a whois query, you agree that you will use this data only for lawful purposes and that, under no circumstances will you use this data to: (1)enable high volume, automated, electronic processes that stress or load this whois database system providing you this information; or (2) allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations via direct mail, electronic mail, or by telephone.  The compilation, repackaging, dissemination or other use of this data is expressly prohibited without prior written consent from us. We reserve the right to modify these terms at any time. By submitting this query, you agree to abide by these terms.For complete domain details go to:http://whois.aliyun.com/whois/domain/hichina.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;/details&gt;

&lt;p&gt;我猜搜尋引擎對網站的排名會隨著 HTTP 3xx 跳轉而轉移。藉由 3xx 跳轉不斷變換網域，能在保留網域排名的情況下不斷規避封鎖清單。或許未來封鎖清單能自動追蹤網域跳轉，在新網域獲得足夠的排名前先行封鎖。&lt;/p&gt;
&lt;p&gt;無論如何，瀏覽器外掛終究需要使用人數多才能發揮效益。這應是過於樂觀了──當這種情況成真時，或許多數人已經了解抵制內容農場的重要性，內容農場的消亡也不遠了。&lt;/p&gt;</content><category term="Security"/></entry><entry><title>[轉載] Containers 系統架構</title><link href="https://chyen.cc/blog/posts/2021/03/23/containers-architecture.html" rel="alternate"/><published>2021-03-23T00:00:00+08:00</published><updated>2021-03-23T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2021-03-23:/blog/posts/2021/03/23/containers-architecture.html</id><summary type="html">&lt;p&gt;Rootless containers 是一項最近還滿常被討論的技術。
比較常見的實作當推 RedHat 主打的 &lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt;。
在學習 Podman 的過程中，被 container 架構的複雜性搞的七葷 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Rootless containers 是一項最近還滿常被討論的技術。
比較常見的實作當推 RedHat 主打的 &lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt;。
在學習 Podman 的過程中，被 container 架構的複雜性搞的七葷八素。
查遍各處，寫的比較完善的中文解說莫屬第 11 屆 iT 邦幫忙鐵人賽參賽作品：&lt;a href="https://ithelp.ithome.com.tw/users/20120317/ironman/2442"&gt;其實我真的沒想過在美國上班也被拉來參加30天分享那些年我怎麼理解 kubernetes 的運作&lt;/a&gt;。
這一系列的文章分門別類講述 container stack 中的不同元件，再輔以歷史的脈絡探討架構演變的過程中，主事者可能的心路歷程。
詳盡而不流於雜亂，且能看出作者的技術功力深厚，個人認為是上乘之作！&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Migrate Android data from an F-Droid app to a Google Play one</title><link href="https://chyen.cc/blog/posts/2021/03/15/migrate-android-app-data.html" rel="alternate"/><published>2021-03-15T00:00:00+08:00</published><updated>2021-03-15T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2021-03-15:/blog/posts/2021/03/15/migrate-android-app-data.html</id><summary type="html">&lt;h1&gt;Motivatation&lt;/h1&gt;
&lt;p&gt;Recently, login for TTRSS-Reader is &lt;a href="https://github.com/nilsbraden/ttrss-reader-fork/issues/439"&gt;broken after a server update&lt;/a&gt;.
A working version is uploaded to Google Play, but it presumably takes a long time for the F-Droid version to be updated. I investigated how to migrate data between Android apps as I cannot live without RSS feeds for …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Motivatation&lt;/h1&gt;
&lt;p&gt;Recently, login for TTRSS-Reader is &lt;a href="https://github.com/nilsbraden/ttrss-reader-fork/issues/439"&gt;broken after a server update&lt;/a&gt;.
A working version is uploaded to Google Play, but it presumably takes a long time for the F-Droid version to be updated. I investigated how to migrate data between Android apps as I cannot live without RSS feeds for one day :D&lt;/p&gt;
&lt;h1&gt;Migration&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clear cache and force stop the application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Backup data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;zcvf&lt;span class="w"&gt; &lt;/span&gt;/data/local/tmp/org.ttrssreader.tar.gz&lt;span class="w"&gt; &lt;/span&gt;*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uninstall the current version (from F-Droid) and install the other version (from Google Play)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find out the new UID and SELinux context&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lZ
&lt;span class="go"&gt;total 28&lt;/span&gt;
&lt;span class="go"&gt;drwxrws--x   2 u0_a270 u0_a270_cache u:object_r:app_data_file:s0:c14,c257,c512,c768  4096 2021-03-15 12:28 cache&lt;/span&gt;
&lt;span class="go"&gt;drwxrws--x   2 u0_a270 u0_a270_cache u:object_r:app_data_file:s0:c14,c257,c512,c768  4096 2021-03-15 12:28 code_cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The new uid is &lt;code&gt;u0_a270&lt;/code&gt; (10270) and the new SELinux label is &lt;code&gt;u:object_r:app_data_file:s0:c14,c257,c512,c768&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restore data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;--exclude&lt;span class="o"&gt;=&lt;/span&gt;cache&lt;span class="w"&gt; &lt;/span&gt;--exclude&lt;span class="o"&gt;=&lt;/span&gt;code_cache&lt;span class="w"&gt; &lt;/span&gt;-zxvf&lt;span class="w"&gt; &lt;/span&gt;/data/local/tmp/org.ttrssreader.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change UID and SELinux context&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;chown&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10270&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;chgrp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10270&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;chown&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10270&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;app_textures/&lt;span class="w"&gt; &lt;/span&gt;app_webview/&lt;span class="w"&gt; &lt;/span&gt;databases/&lt;span class="w"&gt; &lt;/span&gt;files/&lt;span class="w"&gt; &lt;/span&gt;shared_prefs/
&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;chgrp&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10270&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;app_textures/&lt;span class="w"&gt; &lt;/span&gt;app_webview/&lt;span class="w"&gt; &lt;/span&gt;databases/&lt;span class="w"&gt; &lt;/span&gt;files/&lt;span class="w"&gt; &lt;/span&gt;shared_prefs/
&lt;span class="gp"&gt;htc_ocndugl:/data/data/org.ttrssreader # &lt;/span&gt;chcon&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;u:object_r:app_data_file:s0:c14,c257,c512,c768&lt;span class="w"&gt; &lt;/span&gt;app_textures/&lt;span class="w"&gt; &lt;/span&gt;app_webview/&lt;span class="w"&gt; &lt;/span&gt;databases/&lt;span class="w"&gt; &lt;/span&gt;files/&lt;span class="w"&gt; &lt;/span&gt;shared_prefs/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here, the &lt;code&gt;c14,c257,c512,c768&lt;/code&gt; part in a SELinux label is new to me. Reading SELinux documents and tutorials, they seem "categories" for multi-category security (MCS). Few materials discuss how categories are assigned in Android. Here are some references:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://source.android.com/docs/security/features/selinux/concepts"&gt;SELinux concepts&lt;/a&gt; mentions that categories are used to "Isolate the app data from access by another app" and "Isolate the app data from one physical user to another." I guess categories plays a similar role to UIDs like &lt;code&gt;u0_a270&lt;/code&gt; in the DAC world.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SELinuxProject/selinux-notebook/blob/main/src/seandroid.md#entry-definitions"&gt;Security Enhancements for Android&lt;/a&gt; mentions a &lt;code&gt;levelFrom=&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;Furthermore, &lt;a href="https://www.politesi.polimi.it/bitstream/10589/147391/3/2019_04_Rossi.pdf"&gt;SELinux policies for fine-grained protection of Android apps&lt;/a&gt; mentions the relation between &lt;code&gt;levelFrom=&lt;/code&gt; and category numbers. Those numbers do not match what I got from &lt;code&gt;ls -Z&lt;/code&gt; exactly but close. Probably actual category numbers are related on them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SELinux adoption in Android makes things complicated as well as interesting!&lt;/p&gt;</content><category term="misc"/></entry><entry><title>What's inside a backup file of IPMIView on Android?</title><link href="https://chyen.cc/blog/posts/2021/03/07/ipmiview-android.html" rel="alternate"/><published>2021-03-07T00:00:00+08:00</published><updated>2021-03-07T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2021-03-07:/blog/posts/2021/03/07/ipmiview-android.html</id><summary type="html">&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.smc.smcipmitool"&gt;IPMIView&lt;/a&gt; is a handy tool to monitor status of multiple servers at once.
It provides a backup feature to export settings and credentials.
Per &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;bsdtar&lt;/code&gt; commands, the backup file &lt;code&gt;/sdcard/Supermicro/IPMIViewBackup_YYYYMMDDHHMMSS.smc&lt;/code&gt; is a password-protected zip archive.
Assuming the password can be extracted from the APK, I …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.smc.smcipmitool"&gt;IPMIView&lt;/a&gt; is a handy tool to monitor status of multiple servers at once.
It provides a backup feature to export settings and credentials.
Per &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;bsdtar&lt;/code&gt; commands, the backup file &lt;code&gt;/sdcard/Supermicro/IPMIViewBackup_YYYYMMDDHHMMSS.smc&lt;/code&gt; is a password-protected zip archive.
Assuming the password can be extracted from the APK, I started a journey of reverse engineering.&lt;/p&gt;
&lt;p&gt;First, retrieve the APK file for IPMIView:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;pm&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;ipmi
&lt;span class="go"&gt;package:com.smc.smcipmitool&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;pm&lt;span class="w"&gt; &lt;/span&gt;path&lt;span class="w"&gt; &lt;/span&gt;com.smc.smcipmitool
&lt;span class="go"&gt;package:/data/app/com.smc.smcipmitool-MffdfTl97aivo2O1HplX2g==/base.apk&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/data/app/com.smc.smcipmitool-MffdfTl97aivo2O1HplX2g==/base.apk&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;base.apk&lt;span class="w"&gt; &lt;/span&gt;com.smc.smcipmitool.apk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And let &lt;a href="https://ibotpeaches.github.io/Apktool/"&gt;apktool&lt;/a&gt; extract smali codes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;apktool&lt;span class="w"&gt; &lt;/span&gt;d&lt;span class="w"&gt; &lt;/span&gt;com.smc.smcipmitool.apk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, find out which classes are related to the backup file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;rg&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;IPMIViewBackup&lt;span class="w"&gt; &lt;/span&gt;com.smc.smcipmitool
&lt;span class="go"&gt;com.smc.smcipmitool/smali/com/smc/smcipmitool/util/IPMIBackUpAgent$3.smali&lt;/span&gt;
&lt;span class="go"&gt;com.smc.smcipmitool/smali/com/smc/smcipmitool/util/IPMIBackUpAgent.smali&lt;/span&gt;
&lt;span class="go"&gt;com.smc.smcipmitool/smali/com/smc/smcipmitool/util/IPMIBackUpAgent$1.smali&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, searching for 'password' in these files. Bingo!
In &lt;code&gt;com.smc.smcipmitool/smali/com/smc/smcipmitool/util/IPMIBackUpAgent$1.smali&lt;/code&gt;, there is a function call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;move-result-object&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v1&lt;/span&gt;

const-string&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8o2r2c6i5M9r9e0puS&amp;quot;&lt;/span&gt;

invoke-virtual&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;L&lt;/span&gt;com/smc/smcipmitool/util/&lt;span class="nc"&gt;IPMIBackUpAgentHelper&lt;/span&gt;;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;zipDirWithPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;[L&lt;/span&gt;java/io/&lt;span class="nc"&gt;File&lt;/span&gt;;&lt;span class="kt"&gt;L&lt;/span&gt;java/io/&lt;span class="nc"&gt;File&lt;/span&gt;;&lt;span class="kt"&gt;L&lt;/span&gt;java/lang/&lt;span class="nc"&gt;String&lt;/span&gt;;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="kt"&gt;Z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, the password is here. I can use that password to extract the compressed IPMIViewBackup.smc&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;bsdtar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;IPMIViewBackup_YYYYMMDDHHMMSS.smc&lt;span class="w"&gt; &lt;/span&gt;IPMIViewBackup.smc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's yet another zip archive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;bsdtar&lt;span class="w"&gt; &lt;/span&gt;tf&lt;span class="w"&gt; &lt;/span&gt;IPMIViewBackup.smc
&lt;span class="go"&gt;group.xml&lt;/span&gt;
&lt;span class="go"&gt;shared_prefs/&lt;/span&gt;
&lt;span class="go"&gt;shared_prefs/session_pref.xml&lt;/span&gt;
&lt;span class="go"&gt;shared_prefs/com.smc.smcipmitool_preferences.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;IPMI server IPs, encoded usernames and passwords can be found in the file &lt;code&gt;group.xml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Elementary, My Dear Watson. - &lt;a href="https://quoteinvestigator.com/2016/07/14/watson/"&gt;not by Sherlock Holmes&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content><category term="Work"/></entry><entry><title>Removal from HSTS preload list for my domain</title><link href="https://chyen.cc/blog/posts/2020/11/08/hsts-preload-removal.html" rel="alternate"/><published>2020-11-08T14:24:00+08:00</published><updated>2020-11-08T14:24:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2020-11-08:/blog/posts/2020/11/08/hsts-preload-removal.html</id><summary type="html">&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Since long time ago, I host my website on my own server.
As time goes by, the maintaining cost for this website appears to increase indefinitely with complexity from Podman, Ansible, Buildbot and so on.
I have once tried use GitLab pages to host my website.
However, it does …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Since long time ago, I host my website on my own server.
As time goes by, the maintaining cost for this website appears to increase indefinitely with complexity from Podman, Ansible, Buildbot and so on.
I have once tried use GitLab pages to host my website.
However, it does not support &lt;a href="https://gitlab.com/gitlab-org/gitlab-pages/-/issues/28"&gt;adding custom HSTS headers&lt;/a&gt;, so I need to opt-out HSTS preloading if I don't want to see a warning on &lt;a href="https://hstspreload.org"&gt;hstspreload.org&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Timeline&lt;/h1&gt;
&lt;p&gt;Opting-out HSTS preloading is as simple as opting-in.
I just changed the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; header for my server and submit a request at hstspreload.org.
As it is said that the removal can take a long time, I'm curious how long it will.
So, here is the timeline.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2020/11/08 submitted removal request&lt;/li&gt;
&lt;li&gt;2020/11/14 &lt;a href="https://chromium.googlesource.com/chromium/src/net/+/acd756e37a432016a1078a73edecb12ac26f13a6^!/"&gt;Removed from Chromium source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2020/11/17 &lt;a href="https://hg.mozilla.org/mozilla-central/rev/61fadacfb1aff53f8f79cfabe6a6fe5eb03e06fa"&gt;Removed from Firefox source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Security"/></entry><entry><title>Use ECDSA certificate for identification on IRC networks</title><link href="https://chyen.cc/blog/posts/2020/10/17/irc-ecdsa-cert.html" rel="alternate"/><published>2020-10-17T15:56:00+08:00</published><updated>2020-10-17T15:56:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2020-10-17:/blog/posts/2020/10/17/irc-ecdsa-cert.html</id><summary type="html">&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Recently, I noticed that I can no longer make myself as an operator on the OFTC channel #lxqt.
It turns out that my current SSL client certificate has expired a few days ago.
As it seems that there is no way to &lt;a href="https://security.stackexchange.com/a/91513/163400"&gt;extend expiry of an X509 certificate&lt;/a&gt; like …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Recently, I noticed that I can no longer make myself as an operator on the OFTC channel #lxqt.
It turns out that my current SSL client certificate has expired a few days ago.
As it seems that there is no way to &lt;a href="https://security.stackexchange.com/a/91513/163400"&gt;extend expiry of an X509 certificate&lt;/a&gt; like what we can do for &lt;a href="https://chyen.cc/blog/pages/pgp-handling.html"&gt;PGP keys&lt;/a&gt;, I need to regenerate a client certificate.&lt;/p&gt;
&lt;p&gt;Meanwhile, I heard that ECDSA certificates are &lt;a href="https://letsencrypt.org/2020/09/17/new-root-and-intermediates.html"&gt;much smaller than RSA certificates&lt;/a&gt;, resulting in faster TLS handshakes.
Although IRC uses persistent connections, so the performance improvement during handshakes does not matter much, it sounds really cool.
So here is my journey - switching to ECDSA certificates on IRC networks.&lt;/p&gt;
&lt;h2&gt;Steps&lt;/h2&gt;
&lt;p&gt;The first step is generating a pair of ECDSA key and certificate. I use &lt;a href="https://security.stackexchange.com/a/190646/163400"&gt;this one line command&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;openssl&lt;span class="w"&gt; &lt;/span&gt;req&lt;span class="w"&gt; &lt;/span&gt;-nodes&lt;span class="w"&gt; &lt;/span&gt;-newkey&lt;span class="w"&gt; &lt;/span&gt;ec&lt;span class="w"&gt; &lt;/span&gt;-pkeyopt&lt;span class="w"&gt; &lt;/span&gt;ec_paramgen_curve:secp384r1&lt;span class="w"&gt; &lt;/span&gt;-keyout&lt;span class="w"&gt; &lt;/span&gt;irc_client_key.pem&lt;span class="w"&gt; &lt;/span&gt;-x509&lt;span class="w"&gt; &lt;/span&gt;-days&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3650&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-out&lt;span class="w"&gt; &lt;/span&gt;irc_client_cert.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Following steps for ECDSA certificates are the same as those for RSA certificates. I follow &lt;a href="https://www.oftc.net/NickServ/CertFP/"&gt;instructions from OFTC&lt;/a&gt; to create a combined key/certificate file and get its SHA1 fingerprint.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;irc_client_key.pem&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;irc_client_cert.pem
$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;irc_client_cert.pem
$&lt;span class="w"&gt; &lt;/span&gt;openssl&lt;span class="w"&gt; &lt;/span&gt;x509&lt;span class="w"&gt; &lt;/span&gt;-noout&lt;span class="w"&gt; &lt;/span&gt;-fingerprint&lt;span class="w"&gt; &lt;/span&gt;-text&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;irc_client_cert.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Steps for libera.chat are similar, except that &lt;a href="https://libera.chat/guides/certfp"&gt;a SHA512 fingerprint is needed&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;openssl&lt;span class="w"&gt; &lt;/span&gt;x509&lt;span class="w"&gt; &lt;/span&gt;-noout&lt;span class="w"&gt; &lt;/span&gt;-fingerprint&lt;span class="w"&gt; &lt;/span&gt;-sha512&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;irc_client_cert.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Later I need to tell IRC servers about my new fingerprint. As the certificate has expired, I need to identify myself with a password first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/msg NickServ identify not_my_real_password
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then actually add the new fingerprint.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/msg NickServ cert add new_fingerprint_without_colons
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After re-connecting to the IRC server to make sure the new certificate really works, the old fingerprint can be removed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/msg NickServ cert del old_fingerprint_without_colons
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I can make myself as an operator 🎉&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/msg ChanServ op #lxqt yan12125
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After doing whatever channel management work, remove myself as an operator as usual.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/msg ChanServ deop #lxqt yan12125
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Security"/></entry><entry><title>Using WireGuard kernel module for HTC U11</title><link href="https://chyen.cc/blog/posts/2020/02/02/htc-u11-wireguard-kernel-module.html" rel="alternate"/><published>2020-02-02T00:00:00+08:00</published><updated>2020-02-02T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2020-02-02:/blog/posts/2020/02/02/htc-u11-wireguard-kernel-module.html</id><summary type="html">&lt;h1&gt;Background&lt;/h1&gt;
&lt;p&gt;Recently, there are plenty of introductory articles about &lt;a href="https://www.wireguard.com/"&gt;the WireGuard VPN&lt;/a&gt; on &lt;a href="https://www.phoronix.com/scan.php?page=search&amp;amp;q=WireGuard"&gt;Phoronix&lt;/a&gt; as the first stable Linux kernel with WireGuard (5.6) is released.
At first I thought it was nothing but yet another VPN protocol like conventional PPTP or L2TP.
However, after digging into it, I found …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Background&lt;/h1&gt;
&lt;p&gt;Recently, there are plenty of introductory articles about &lt;a href="https://www.wireguard.com/"&gt;the WireGuard VPN&lt;/a&gt; on &lt;a href="https://www.phoronix.com/scan.php?page=search&amp;amp;q=WireGuard"&gt;Phoronix&lt;/a&gt; as the first stable Linux kernel with WireGuard (5.6) is released.
At first I thought it was nothing but yet another VPN protocol like conventional PPTP or L2TP.
However, after digging into it, I found that it is different by design.
&lt;em&gt;It is quite simple.&lt;/em&gt;
WireGuard tools only create the VPN by adding a network interface and assign the IP and the gateway.
There is neither NAT nor DHCP.
So, I decided to give it a try.&lt;/p&gt;
&lt;h1&gt;Using WireGuard on Android&lt;/h1&gt;
&lt;h2&gt;A first taste&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://play.google.com/store/apps/details?id=com.wireguard.android"&gt;official App&lt;/a&gt; already includes a Go backend, so enabling WireGuard on Android is as simple as installing an application and deploying an configuration file.
However, there are some limitations with the default Go backend.
It utilizes Android's VPN framework to tunnel traffic.
As a result, there is always a VPN icon in the notification area, and I cannot connect to another VPN while keeping the WireGuard VPN connected.
For phones with root access, a solution is using the Linux kernel module backend, so that limitations imposed by Android frameworks are gone.&lt;/p&gt;
&lt;h2&gt;Building the kernel module&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://forum.xda-developers.com/android/development/wireguard-rom-integration-t3711635"&gt;zx2c4's article on XDA&lt;/a&gt; includes descriptions for how to build the WireGuard kernel module for Android devices.
That post mentioned &lt;a href="https://git.zx2c4.com/android_kernel_wireguard/"&gt;the android_kernel_wireguard repo&lt;/a&gt;, which is apparently an AOSP project as there is a top-level &lt;code&gt;Android.mk&lt;/code&gt; file.
As per &lt;code&gt;patch_kernel.sh&lt;/code&gt; in that AOSP repo, I need to inject &lt;a href="https://git.zx2c4.com/wireguard-linux-compat"&gt;wireguard-linux-compat&lt;/a&gt; into the kernel source tree for my device as an in-kernel module.
After the injection, I can build the whole kernel to get the WireGuard kernel module.
To simplify things, I decided to try out building a vanilla kernel image first.&lt;/p&gt;
&lt;h3&gt;Building a vanilla kernel&lt;/h3&gt;
&lt;p&gt;Fortunately, HTC obeys GPL obligations and makes source code tarballs for kernels used on devices available on &lt;a href="https://www.htcdev.com/devcenter/downloads"&gt;htcdev.com&lt;/a&gt;.
First, let's download and extract kernel sources provided by HTC.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://dl4.htc.com/RomCode/Source_and_Binaries/ocndtwl-4.4.153-perf-g0041d80.tar.gz&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Referer: https://www.htcdev.com/devcenter/downloads&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-O
$&lt;span class="w"&gt; &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;xf&lt;span class="w"&gt; &lt;/span&gt;ocndtwl-4.4.153-perf-g0041d80.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Among extracted files, there is a &lt;code&gt;Readme.txt&lt;/code&gt; with build instructions.
Basically, I should be able to build the whole kernel by following those instructions.
However, there are two tricks.
First, the kernel is built with GCC, while Android NDK &lt;a href="https://github.com/android/ndk/wiki/Changelog-r18"&gt;has phased out GCC&lt;/a&gt;.
As a result, the latest revision of https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9 contains only binutils but not GCC.
To work around the issue, I used an older branch for the compiler repo.
Second, the kernel provided by HTC is too old, so that host scripts cannot be compiled with GCC 10 or newer as the latter &lt;a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85678"&gt;forbids common symbols from different source files&lt;/a&gt;.
Thanks to &lt;a href="https://github.com/WireGuard/android-wireguard-module-builder/pull/6#issuecomment-833692039"&gt;the hint given by Nathan Chancellor&lt;/a&gt;, I can apply a simple patch to make things build. Here are all steps:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;kernel
$&lt;span class="w"&gt; &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;xf&lt;span class="w"&gt; &lt;/span&gt;ocndtwl-4.4.153-perf-g0041d80.tar.gz
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;ndk-r13-release&lt;span class="w"&gt; &lt;/span&gt;--depth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kernel
$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;out
$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/patch/?id=ce513359d8507123e63f34b56e67ad558074be22&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;gcc10.patch
$&lt;span class="w"&gt; &lt;/span&gt;patch&lt;span class="w"&gt; &lt;/span&gt;-Np1&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;gcc10.patch
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;/../aarch64-linux-android-4.9/bin/aarch64-linux-android-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;htcperf_defconfig
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;/../aarch64-linux-android-4.9/bin/aarch64-linux-android-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;-j3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Build the WireGuard module with the vanilla kernel&lt;/h3&gt;
&lt;p&gt;After building the kernel, I started to integrate steps for building my kernel module into the scripts for building WireGuard kernel modules.
By some googling, I found https://github.com/WireGuard/android-wireguard-module-builder, which contains scripts for building hosted kernel modules on &lt;a href="https://download.wireguard.com/android-module/"&gt;the download server&lt;/a&gt; used by the WireGuard Android application.
In that repository, &lt;code&gt;build-one.bash&lt;/code&gt; uses &lt;code&gt;repo&lt;/code&gt; to build the kernel module using &lt;code&gt;do.bash&lt;/code&gt; and &lt;code&gt;manifest.xml&lt;/code&gt; in &lt;code&gt;kernels/&amp;lt;codename&amp;gt;&lt;/code&gt;.
Here &lt;code&gt;manifest.xml&lt;/code&gt; is an AOSP platform manifest just like those mentioned on &lt;a href="https://source.android.com/setup/build/building-kernels?hl=en"&gt;official build instructions for Android kernels&lt;/a&gt;.
In existing scripts, &lt;code&gt;do.bash&lt;/code&gt; calls &lt;code&gt;build.sh&lt;/code&gt; in &lt;a href="https://android.googlesource.com/kernel/build"&gt;the AOSP kernel/build project&lt;/a&gt;.
However, as HTC provides only tarballs instead of a complete AOSP tree, I decided to put all commands, include injecting the wireguard-linux-compat module into the kernel source tree and building the whole kernel, directly into &lt;code&gt;do.bash&lt;/code&gt;.
I still need a git repository as AOSP &lt;code&gt;manifest.xml&lt;/code&gt; does not seem to accept tarballs.
By some googling, I found the maintainer behind the TWRP port for my device publishes &lt;a href="https://github.com/CaptainThrowback/android_kernel_htc_ocn"&gt;a git repo&lt;/a&gt; with mostly the same files as downloaded from htcdev.com.
After some trials, I managed to create &lt;code&gt;manifest.xml&lt;/code&gt; that specifies an older branch of the compiler repo and use a non-Google git repository.
The overall results are submitted as &lt;a href="https://github.com/WireGuard/android-wireguard-module-builder/pull/3"&gt;a pull request&lt;/a&gt; and it got merged.
Now I can reproduce the kernel module with a simple step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;./build-one.sh&lt;span class="w"&gt; &lt;/span&gt;ocn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A few months later, I was hit by the GCC 10 issue mentioned above, so I created &lt;a href="https://github.com/WireGuard/android-wireguard-module-builder/pull/6"&gt;another pull request&lt;/a&gt; to get it fixed.&lt;/p&gt;
&lt;h2&gt;Deploy the built kernel module&lt;/h2&gt;
&lt;p&gt;The kernel module built by scripts mentioned above has a hash string in its name.
That hash can be computed by running &lt;code&gt;sha256sum /proc/version|cut -d ' ' -f 1&lt;/code&gt; on the device as &lt;a href="https://github.com/WireGuard/android-wireguard-module-builder#adding-your-phones-kernel"&gt;hinted&lt;/a&gt; on the module-builder repo.
Moreover, if the kernel module exists in the &lt;a href="https://github.com/WireGuard/wireguard-android/blob/0.0.20200124/app/src/main/java/com/wireguard/android/util/ModuleLoader.java"&gt;download path&lt;/a&gt; used by the Android application, it will load that module as if the latter is downloaded from the official download server.
Above all, I can place the locally-built kernel module to where the application expected, and click in the Android application to use the module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;out/wireguard-80ee34126cd97c9a15bc3b970a6f38ce30852d0b8547dbcc43eee22956aa1934.ko&lt;span class="w"&gt; &lt;/span&gt;/data/local/tmp/
$&lt;span class="w"&gt; &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;shell
$&lt;span class="w"&gt; &lt;/span&gt;su
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/data/data/com.wireguard.android/cache
$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;kmod
$&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;/data/local/tmp/wireguard-80ee34126cd97c9a15bc3b970a6f38ce30852d0b8547dbcc43eee22956aa1934.ko&lt;span class="w"&gt; &lt;/span&gt;kmod/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After my pull request is merged, the kernel module for my device appeared on the official download server.
To verify it, I cleared the application cache and then click the "Download and install kernel module" again without pushing the locally-built module beforehand.
Confirmed working!&lt;/p&gt;
&lt;h1&gt;Random feelings&lt;/h1&gt;
&lt;p&gt;It is &lt;a href="https://gist.github.com/yan12125/78a9004acb1bed5faf2ffd442163e2ef"&gt;not my first time&lt;/a&gt; to build a Android kernel, but it is my first time to interact with professional Android kernel developers for such a bleeding-edge and exciting feature.
I learned a lot, and I am looking forward to contributing more to AOSP!&lt;/p&gt;</content><category term="Something fun"/></entry><entry><title>Migrating Windows 10 from VirtualBox to LibVirt</title><link href="https://chyen.cc/blog/posts/2020/01/16/libvirt-win10.html" rel="alternate"/><published>2020-01-16T22:50:00+08:00</published><updated>2020-01-16T22:50:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2020-01-16:/blog/posts/2020/01/16/libvirt-win10.html</id><summary type="html">&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I'd like to run Android emulators on my PC for unit tests of &lt;a href="https://github.com/yan12125/python3-android"&gt;python3-android&lt;/a&gt;.
However, Android emulators failed to run as the same host already runs a Windows VM using VirtualBox, and VirtualBox VMs cannot run together with KVM-based ones like &lt;a href="https://stackoverflow.com/a/34054673/3786245"&gt;recent Android system images for x86/x86_64&lt;/a&gt;.
Thinking …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I'd like to run Android emulators on my PC for unit tests of &lt;a href="https://github.com/yan12125/python3-android"&gt;python3-android&lt;/a&gt;.
However, Android emulators failed to run as the same host already runs a Windows VM using VirtualBox, and VirtualBox VMs cannot run together with KVM-based ones like &lt;a href="https://stackoverflow.com/a/34054673/3786245"&gt;recent Android system images for x86/x86_64&lt;/a&gt;.
Thinking twice, I decided to move away from VirtualBox and try out LibVirt, which uses qemu under the hood and is comaptible with Android emulators.&lt;/p&gt;
&lt;h1&gt;Steps&lt;/h1&gt;
&lt;p&gt;Mostly I follow &lt;a href="https://williamlinuxnotes.blogspot.com/2016/12/virtualbox-kvm.html"&gt;a Chinese blog post&lt;/a&gt;.
A tricky point is about permissions.
&lt;code&gt;libvirtd&lt;/code&gt;, the daemon for LibVirt, &lt;a href="https://github.com/libvirt/libvirt/blob/v5.10.0/src/remote/libvirtd.rules"&gt;uses PolicyKit&lt;/a&gt; to restrict connections to authenticated users only.
Either I need to add myself to the &lt;code&gt;libvirt&lt;/code&gt; group as described in the aforementioned blog post, or I need to enter my password whenever I want to connect to LibVirt.
In the end, I found that the &lt;code&gt;remote-viewer&lt;/code&gt; command from the &lt;a href="https://archlinux.org/packages/community/x86_64/virt-viewer/"&gt;virt-viewer package&lt;/a&gt; allows connecting to virtual machines without bothering with authentication.
As I seldom modify settings of virtual machines, I didn't add myself to the &lt;code&gt;libvirt&lt;/code&gt; group, following &lt;a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege"&gt;the principle of least priviledge&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Performance tuning&lt;/h1&gt;
&lt;p&gt;Like VirtualBox, default configuration in a LibVirt VM provides hardware simulation that can boot Windows without extra steps, while the performance is awful, especially for disk I/O and graphics.
To get better performance, I set up &lt;a href="https://www.linux-kvm.org/page/Virtio"&gt;VirtIO&lt;/a&gt; to enjoy the benefit of paravirtualization.&lt;/p&gt;
&lt;h2&gt;Enabling VirtIO-backed disks&lt;/h2&gt;
&lt;p&gt;This is somehow tricky as Windows does not seem to install the virtio driver for the disk controller if I boot the VM with the default disk controller, and if I switch to the VirtIO-based disk controller, Windows does not boot, showing an error &lt;code&gt;INACCESSIBLE BOOT DEVICE&lt;/code&gt;.
In the end, I install the driver for the disk controller in the &lt;a href="https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-recovery-environment--windows-re--technical-reference"&gt;Windows Recovery Environment (Windows RE)&lt;/a&gt;.
First, mount the &lt;a href="https://aur.archlinux.org/packages/virtio-win/"&gt;VirtIO ISO&lt;/a&gt; to the VM before booting.
During boot, hit F8 repeatedly until Windows RE is reached.
In a command prompt, I first install the VirtIO driver to the running Windows RE system (assuming the VirtIO ISO is mounted on &lt;code&gt;D:\&lt;/code&gt;),&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;\&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;drvload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;\&lt;span class="n"&gt;viostor&lt;/span&gt;\&lt;span class="n"&gt;w10&lt;/span&gt;\&lt;span class="n"&gt;amd64&lt;/span&gt;\&lt;span class="n"&gt;viostor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then I can reach my Windows installation on the VirtIO-based disk and install the driver into the normal system (assuming mounted on &lt;code&gt;E:\&lt;/code&gt;),&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;C:\&amp;gt;dism /image:E: /add-driver /driver:D:\viostor\w10\amd64\viostor.inf`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other hardware components, including the QXL graphics controller and the VirtIO ethernet adapter, are much easier to setup as they can be configured with GUI tools on a running Windows instance.&lt;/p&gt;
&lt;h2&gt;CPU usage tuning&lt;/h2&gt;
&lt;p&gt;Another performance issue I noticed is that CPU utilization of the qemu process is quite high when VM is idle.
I remember the value is &amp;lt; 10% for a VirtualBox VM.
Google brings me to &lt;a href="https://www.reddit.com/r/VFIO/comments/80p1q7/high_kvmqemu_cpu_utilization_when_windows_10/"&gt;a Reddit post&lt;/a&gt;, and after disabling &lt;a href="https://en.wikipedia.org/wiki/High_Precision_Event_Timer"&gt;HPET&lt;/a&gt;, the CPU usage went down to the normal range.&lt;/p&gt;
&lt;h4&gt;UPDATE&lt;/h4&gt;
&lt;p&gt;After some time I noticed that the CPU usage of the qemu process is quite high again.
It often goes beyond 50%.
Using the &lt;code&gt;perf&lt;/code&gt; command mentioned by &lt;a href="https://www.reddit.com/user/tholin/"&gt;/u/tholin&lt;/a&gt; in the Reddit post above, I found that the &lt;code&gt;vmx_l1d_flush&lt;/code&gt; function takes some time.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ sudo perf kvm --host top -p `pidof qemu-system-x86_64`
4.84%  [kernel]                    [k] vmx_l1d_flush
3.33%  [kernel]                    [k] __fget_files
2.75%  [kernel]                    [k] native_write_msr
2.62%  [kernel]                    [k] do_sys_poll
2.30%  [kernel]                    [k] _raw_spin_lock_irqsave
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The function with the highest overhead &lt;code&gt;vmx_l1d_flush&lt;/code&gt; seems to be related to &lt;a href="https://en.wikipedia.org/wiki/Foreshadow"&gt;L1TF&lt;/a&gt;.
From &lt;a href="https://github.com/torvalds/linux/blob/v5.11/arch/x86/kvm/vmx/vmx.c#L360"&gt;kernel source codes&lt;/a&gt;, this function is controlled by a module kernel parameter &lt;code&gt;vmentry_l1d_flush&lt;/code&gt;.
From &lt;a href="https://wiki.ubuntu.com/SecurityTeam/KnowledgeBase/L1TF"&gt;this Ubuntu wiki page&lt;/a&gt;, it can be disabled by the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;never&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;/sys/module/kvm_intel/parameters/vmentry_l1d_flush
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After shutting down the VM, setting the module parameter and restarting the VM, CPU usage of the qemu process goes back to under 10% most of time.
As it is a security mitigation, I'll reverted to the default and keep it until I observed real performance bottlenecks.&lt;/p&gt;
&lt;h2&gt;Shared folder performance&lt;/h2&gt;
&lt;p&gt;I used the shared folders feature of VirtualBox to allow virtual machines to access files on the host.
The equivalent feature in LibVirt seems VirtIO-FS, while &lt;a href="https://virtio-fs.gitlab.io/howto-windows.html"&gt;it requires the WinFSP kernel module&lt;/a&gt;, and I had some issues using &lt;a href="https://github.com/billziss-gh/sshfs-win/"&gt;SSHFS-Win&lt;/a&gt; with WinFSP.
Instead, I choose to deploy a Samba server on the host.
A performance bottleneck I noticed is that when the first Office file is opened after booting, there is a long delay with several seconds.
Using WireShark, I found connection attemps to port 80 from the VM to the host using &lt;code&gt;User-Agent: DavClnt&lt;/code&gt;, and there is already &lt;a href="https://blog.didierstevens.com/2017/11/13/webdav-traffic-to-malicious-sites/"&gt;a blog post&lt;/a&gt; about it.
It seems the WebClient service is still started if I set its startup type to manual, so I disable the service altogether, and there is no longer delay when files are opened.&lt;/p&gt;
&lt;h1&gt;Memories about VirtualBox&lt;/h1&gt;
&lt;p&gt;My experience with VirtualBox was great.
I had used it on Windows, macOS and Linux, and there are only minor hiccups.
Host kernel modules for VirtualBox is out of tree, and thus upgrading the kernel requires additional care.
VirtualBox guest additions follow the same release schedule as the main VirtualBox program.
As a result there were often Windows notifications about upgrading it.
Furthermore, the extension pack for VirtualBox is not open source, and I need to manually package it often.
With LibVirt and qemu, those minor issues are gone as KVM is built into the kernel, VirtIO Windows drivers are not updated often, and the open source version of LibVirt is already feature complete for me.
To me, LibVirt is not substantially better, but I guess I will not go back to VirtualBox :)&lt;/p&gt;</content><category term="Something fun"/></entry><entry><title>A workaround for CCStudio 9.2 UI issue - black backgrounds in tooltips</title><link href="https://chyen.cc/blog/posts/2019/12/01/ccstudio-tooltip-black-background.html" rel="alternate"/><published>2019-12-01T12:54:00+08:00</published><updated>2019-12-01T12:54:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2019-12-01:/blog/posts/2019/12/01/ccstudio-tooltip-black-background.html</id><summary type="html">&lt;p&gt;&lt;a href="https://www.ti.com/tool/CCSTUDIO"&gt;CCStudio&lt;/a&gt; is what our lab use for developing software on &lt;a href="https://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview.html"&gt;TI MSP430&lt;/a&gt; board. Recently, version 9.2.0 of CCStudio is out, and I noticed that its UI now uses GTK+3 instead of GTK+2. In general this is a good change as GTK+3 has a better look-and-feel …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://www.ti.com/tool/CCSTUDIO"&gt;CCStudio&lt;/a&gt; is what our lab use for developing software on &lt;a href="https://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview.html"&gt;TI MSP430&lt;/a&gt; board. Recently, version 9.2.0 of CCStudio is out, and I noticed that its UI now uses GTK+3 instead of GTK+2. In general this is a good change as GTK+3 has a better look-and-feel by default than GTK+2. However, soon I noticed an issue. Tooltips, which appear when you hover around functions or variables in the code editor, have black backgrounds. As texts in tooltips are also black or in similar colors, tooltips are not readable.&lt;/p&gt;
&lt;p&gt;I wonder if this is an incompatibility with CCStudio and GTK+3. First I tried uninstalling &lt;a href="https://www.archlinux.org/packages/extra/x86_64/gtk3/"&gt;gtk3&lt;/a&gt; from my system. Yes the UI goes back to GTK+2 and tooltips are readable again. Obviously it is not a long-term fix as several other software like Firefox need it. I took some time to study how CCStudio selects between GTK+2 and GTK+3. First I looked at files loaded by the ccstudio process via lsof. There are some *gtk*.so files, and libswt-pi-gtk-4880.so, which is from &lt;a href="https://www.eclipse.org/swt/"&gt;Eclipse SWT&lt;/a&gt;, seems the most relevant. The source code of SWT can be found &lt;a href="https://git.eclipse.org/c/platform/eclipse.platform.swt.git/"&gt;here&lt;/a&gt;. As the SWT version used in CCStudio is &lt;code&gt;3.107.0.v20180611-0422&lt;/code&gt;, I picked git tag &lt;code&gt;I20180611-0500&lt;/code&gt; and searched for &lt;a href="https://wiki.eclipse.org/SWT/Devel/Gtk/GtkVersion"&gt;the configuration key for the GTK+ version&lt;/a&gt; in this repository. After some code reading, I found that an environment variable &lt;code&gt;SWT_GTK3&lt;/code&gt; &lt;a href="https://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/bundles/org.eclipse.swt/Eclipse%20SWT%20PI/gtk/org/eclipse/swt/internal/gtk/OS.java?h=I20180611-0500#n76"&gt;controls whether SWT prefers GTK+3 or GTK+2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, a long-term fix is simple - starting CCStudio with &lt;code&gt;SWT_GTK3=0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Update 2020/01/08 - the issue appears to be fixed in CCStudio 9.3.&lt;/p&gt;</content><category term="misc"/></entry><entry><title>「臺灣學術網路網路語音交換平臺」憑證錯誤的問題</title><link href="https://chyen.cc/blog/posts/2019/11/22/voip-tanet-edu-tw.html" rel="alternate"/><published>2019-11-22T11:00:00+08:00</published><updated>2019-11-22T11:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2019-11-22:/blog/posts/2019/11/22/voip-tanet-edu-tw.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="https://voip.tanet.edu.tw/"&gt;臺灣學術網路網路語音交換平臺&lt;/a&gt; 是一個提供 &lt;a class="reference external" href="https://zoom.us/"&gt;ZOOM&lt;/a&gt; 服務的平臺。用 eduroam 帳號登入並登記，就可以三人以上使用 ZOOM 超過 40 分鐘。LAB …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://voip.tanet.edu.tw/"&gt;臺灣學術網路網路語音交換平臺&lt;/a&gt; 是一個提供 &lt;a class="reference external" href="https://zoom.us/"&gt;ZOOM&lt;/a&gt; 服務的平臺。用 eduroam 帳號登入並登記，就可以三人以上使用 ZOOM 超過 40 分鐘。LAB 遠端 meeting 滿常用這個平台。&lt;/p&gt;
&lt;p&gt;前幾週這個平臺的入口網站憑證過期了。留言反應後他們很快就換上新的憑證。但新的憑證有個問題：缺乏 intermediate CA 的憑證，導致 Firefox 無法連線。我再次留言反應，得到的回覆是他們用 Firefox、Chrome 等等各大瀏覽器都能連線，請我確認我的 Firefox 是否有問題。的確，新安裝的 Chrome，也能正確處理缺乏 intermediate CA 的問題，那為何 Firefox 不行呢？&lt;/p&gt;
&lt;p&gt;查了一下，有個功能叫做 &lt;a class="reference external" href="https://www.thesslstore.com/blog/aia-fetching/"&gt;AIA fetching&lt;/a&gt; ，也有人叫他 AIA chasing ( 例如 &lt;a class="reference external" href="https://bugs.python.org/issue18617"&gt;此Python ticket&lt;/a&gt; )，可以解決缺乏 intermediate CA 的問題。看起來這功能是從憑證的「授權資訊存取」( Authority Information Access, AIA ) 這個欄位取得憑證簽發者，也就是 intermediate CA 的憑證。平臺網站用的憑證是有AIA這個欄位的，然而 &lt;a class="reference external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=399324"&gt;Firefox 並不打算支援 AIA&lt;/a&gt; ，目前只好自立更生，利用 Firefox 會快取 intermediate CA 憑證的特性，先訪問一個和這個平台網站有相同 intermediate CA (&amp;quot;政府伺服器數位憑證管理中心 - G1&amp;quot;) 的網站 ( 例如： &lt;a class="reference external" href="https://property.tycg.gov.tw/"&gt;桃園市政府財產管理系統&lt;/a&gt; )，來讓缺乏 intermediate CA 的憑證可以通過驗證。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>What are public_key fields in GitHub gpg_keys API?</title><link href="https://chyen.cc/blog/posts/2019/08/29/github-api-gpg-public-key.html" rel="alternate"/><published>2019-08-29T20:00:00+08:00</published><updated>2019-08-29T20:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2019-08-29:/blog/posts/2019/08/29/github-api-gpg-public-key.html</id><summary type="html">&lt;p&gt;GitHub provides an API endpoint for querying users' PGP keys. For example,
information about my keys can be found &lt;a class="reference external" href="https://api.github.com/users/yan12125/gpg_keys"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this JSON document, the &lt;cite&gt;raw_key&lt;/cite&gt; is what programs like GnuPG need. What
about &lt;cite&gt;public_key&lt;/cite&gt; fields?  &lt;a class="reference external" href="https://developer.github.com/v3/users/gpg_keys/"&gt;GitHub official document&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
The data returned in the &lt;cite&gt;public_key&lt;/cite&gt; response field is not …&lt;/blockquote&gt;</summary><content type="html">&lt;p&gt;GitHub provides an API endpoint for querying users' PGP keys. For example,
information about my keys can be found &lt;a class="reference external" href="https://api.github.com/users/yan12125/gpg_keys"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this JSON document, the &lt;cite&gt;raw_key&lt;/cite&gt; is what programs like GnuPG need. What
about &lt;cite&gt;public_key&lt;/cite&gt; fields?  &lt;a class="reference external" href="https://developer.github.com/v3/users/gpg_keys/"&gt;GitHub official document&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
The data returned in the &lt;cite&gt;public_key&lt;/cite&gt; response field is not a GPG formatted
key. When a user uploads a GPG key, it is parsed and the cryptographic
public key is extracted and stored. This cryptographic key is what is
returned by the APIs on this page. This key is not suitable to be used
directly by programs like GPG.&lt;/blockquote&gt;
&lt;p&gt;With the help of the &lt;a class="reference external" href="https://github.com/spiegel-im-spiegel/gpgpdump"&gt;gpgpdump&lt;/a&gt; tool, I found that contents in &lt;cite&gt;public_key&lt;/cite&gt;
fields are actually base64-encoded OpenPGP packets, which are defined in &lt;a class="reference external" href="https://tools.ietf.org/html/rfc4880"&gt;RFC
4880&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;https://api.github.com/users/yan12125/gpg_keys&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.[0].public_key&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;base64&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./gpgpdump
Public-Key&lt;span class="w"&gt; &lt;/span&gt;Packet&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;tag&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1198&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;Version:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;current&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;Public&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;creation&lt;span class="w"&gt; &lt;/span&gt;time:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2016&lt;/span&gt;-04-06T02:36:01+08:00
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;91&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;Public-key&lt;span class="w"&gt; &lt;/span&gt;Algorithm:&lt;span class="w"&gt; &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Digital&lt;span class="w"&gt; &lt;/span&gt;Signature&lt;span class="w"&gt; &lt;/span&gt;Algorithm&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;pub&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3072&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;q&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;q&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;prime&lt;span class="w"&gt; &lt;/span&gt;divisor&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;p-1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;g&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3070&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;DSA&lt;span class="w"&gt; &lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;g^x&lt;span class="w"&gt; &lt;/span&gt;mod&lt;span class="w"&gt; &lt;/span&gt;p&lt;span class="w"&gt; &lt;/span&gt;where&lt;span class="w"&gt; &lt;/span&gt;x&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3069&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As an OpenPGP key is composed of a series of OpenPGP packets, it is
theoretically possible to reconstruct a key for verifying stuffs. To achieve
that, an extra user ID packet and a GnuPG patch are needed. The following
Python 3 script can be used to generate a user ID packet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;TAG_UID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;

&lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;foo@example.com&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# RFC 4880, Sec 4.2.1.  Old Format Packet Lengths&lt;/span&gt;
&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mh"&gt;0x80&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAG_UID&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ascii&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the following GnuPG patch forces verification even if there are no
signatures.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;diff --git a/g10/sig-check.c b/g10/sig-check.c&lt;/span&gt;
&lt;span class="gh"&gt;index 4c172d692..eb4653535 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/g10/sig-check.c&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/g10/sig-check.c&lt;/span&gt;
&lt;span class="gu"&gt;@@ -177,7 +177,7 @@ check_signature2 (ctrl_t ctrl,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                 gnupg_compliance_option_string (opt.compliance));
&lt;span class="w"&gt; &lt;/span&gt;      rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="gd"&gt;-  else if (!pk-&amp;gt;flags.valid)&lt;/span&gt;
&lt;span class="gi"&gt;+  else if (0)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;      /* You cannot have a good sig from an invalid key.  */
&lt;span class="w"&gt; &lt;/span&gt;      rc = gpg_error (GPG_ERR_BAD_PUBKEY);
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In practice, no self-signatures simply means the key cannot be trusted. So this
is just a fun experiment :)&lt;/p&gt;
&lt;p&gt;P.S. I've also posted similar contents to &lt;a class="reference external" href="https://stackoverflow.com/a/57712714/3786245"&gt;https://stackoverflow.com/a/57712714/3786245&lt;/a&gt;.&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>GnuTLS無法連上部分台灣網站的問題</title><link href="https://chyen.cc/blog/posts/2018/09/23/gnutls-twca.html" rel="alternate"/><published>2018-09-23T18:52:00+08:00</published><updated>2018-09-23T18:52:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-09-23:/blog/posts/2018/09/23/gnutls-twca.html</id><summary type="html">&lt;p&gt;TL;DR - 請看最後一段的workaround。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;今天台大計中的SSLVPN client pulse-secure升級到9.0r2.0了，我更新完之後測了一下，發現連登入的頁面都連不上&lt;/p&gt;
&lt;p&gt;"Unacceptable TLS certificate"&lt;/p&gt;
&lt;p&gt;於是開 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;TL;DR - 請看最後一段的workaround。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;今天台大計中的SSLVPN client pulse-secure升級到9.0r2.0了，我更新完之後測了一下，發現連登入的頁面都連不上&lt;/p&gt;
&lt;p&gt;"Unacceptable TLS certificate"&lt;/p&gt;
&lt;p&gt;於是開始了一番debug...&lt;/p&gt;
&lt;p&gt;由於這個錯誤訊息在pulse-secure的檔案裡沒有，因此懷疑是&lt;a href="https://webkitgtk.org/"&gt;WebKitGTK&lt;/a&gt;的問題。用&lt;a href="https://github.com/lantw44/ceiba-dl/"&gt;ceiba-dl&lt;/a&gt;測了一下，果然是這樣。&lt;/p&gt;
&lt;p&gt;把source code們拿出來探究一番，從WebKitGTK，libsoup，glib-networking，最後到GnuTLS和p11-kit...&lt;/p&gt;
&lt;p&gt;經過追查，確認是當年&lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=810133"&gt;TWCA提交給Mozilla的證書&lt;/a&gt;有問題，而GnuTLS檢查過於嚴格，才導致連線失敗。&lt;/p&gt;
&lt;p&gt;(詳細技術內容可參考&lt;a href="https://gitlab.com/gnutls/gnutls/issues/569"&gt;我回報給GnuTLS的issue&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;這次感想有兩點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一次看到一個certificate chain裡的intermediate CA同時也是個root CA的，腦筋有點轉不太過來&lt;/li&gt;
&lt;li&gt;台灣廠商不EY(?)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Workaround很簡單，把TWCA Global Root CA從&lt;code&gt;mozilla.trust.p11-kit&lt;/code&gt;這個檔案移除，再跑一遍&lt;code&gt;sudo trust extract-compat&lt;/code&gt;就好了。&lt;/p&gt;</content><category term="misc"/></entry><entry><title>犀利仁師觀後雜感</title><link href="https://chyen.cc/blog/posts/2018/08/05/%E7%8A%80%E5%88%A9%E4%BB%81%E5%B8%AB.html" rel="alternate"/><published>2018-08-05T20:38:00+08:00</published><updated>2018-08-05T20:38:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-08-05:/blog/posts/2018/08/05/犀利仁師.html</id><summary type="html">&lt;p&gt;前陣子整理我的歌曲收藏。其中一首「不能說出口的諾言」沒什麼印象是從那邊聽到的。一查之下是2014年的電視劇犀利仁師。&lt;/p&gt;
&lt;p&gt;這幾天找了些時間在 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;前陣子整理我的歌曲收藏。其中一首「不能說出口的諾言」沒什麼印象是從那邊聽到的。一查之下是2014年的電視劇犀利仁師。&lt;/p&gt;
&lt;p&gt;這幾天找了些時間在YouTube把犀利仁師44集都看完了。裡面我印象最深刻的角色是宋文文，最讓我牽腸掛肚的也是宋文文和范大同一波三折的愛情。這應該是我看過這麼多電視劇以來，唯一對配角的印象比主角還深刻的。&lt;/p&gt;
&lt;p&gt;范大同一開始還以為自己有斷袖之癖，後來發現真相才釋懷。這讓我想到花千骨小說裡的軒轅朗，和女扮男裝的花千骨也有類似情節。不過後者不是主線劇情，作者沒有多加著墨，我就沒有特別感觸了。&lt;/p&gt;
&lt;p&gt;這齣戲有兩個地方讓我哭出來。一個是演完梁祝的時候。戲本身還好，但是看到大家都在哭就很想哭。第二個是柳傲天被打80大板的時候。聽說4 50下就被打死的不少，打80大板實在太慘了...&lt;/p&gt;
&lt;p&gt;有個小點想吐槽。宋文文歷經杖刑，恢復女子之身後，穿的是和慕容月一樣風格的大家閨秀，讓我感覺很違和。依宋文文的性格，感覺路先生那種路線的服裝會比較適合點。&lt;/p&gt;
&lt;p&gt;而看完一長串的電視劇，都會有的後遺症這次也沒少。這幾天看到鬼鬼的照片腦海理都會浮現宋文文的男裝形象。在想事情的時候，「不行」也會變成帶尾音的北京腔XD&lt;/p&gt;</content><category term="misc"/></entry><entry><title>解決macOS分享的Wi-Fi使Line Points無法使用的問題</title><link href="https://chyen.cc/blog/posts/2018/07/07/mac-wifi-line.html" rel="alternate"/><published>2018-07-07T01:18:00+08:00</published><updated>2018-07-07T01:18:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-07-07:/blog/posts/2018/07/07/mac-wifi-line.html</id><summary type="html">&lt;p&gt;Line Points的頁面用Linux hostapd分享出來的Wi-Fi可以正常運作，然而用macOS的網路分享功能無法瀏覽。這個問題困擾了我好久。之前要開Line Points都是先切換成4G，等到要下載App或看影片再換回Wi-Fi。&lt;/p&gt;
&lt;p&gt;某天在PTT上看到有人&lt;a href="https://www.ptt.cc/bbs/Android/M.1529553241.A.5C3.html"&gt;Wi-Fi環境下用Line無法上傳圖片&lt;/a&gt;。鄉民建議改&lt;a href="https://zh.wikipedia.org/wiki/%E6%9C%80%E5%A4%A7%E4%BC%A0%E8%BE%93%E5%8D%95%E5%85%83"&gt;MTU&lt;/a&gt;。我參考&lt;a href="http://awei791129.pixnet.net/blog/post/58092525-%5B%E7%B6%B2%E8%B7%AF%E5%84%AA%E5%8C%96%5D-%E5%A6%82%E4%BD%95%E6%9C%80%E4%BD%B3%E5%8C%96-mtu-%E5%A4%A7%E5%B0%8F-for-mac-os-x-%E5%92%8C-w"&gt;這篇&lt;/a&gt;找出最佳MTU值 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Line Points的頁面用Linux hostapd分享出來的Wi-Fi可以正常運作，然而用macOS的網路分享功能無法瀏覽。這個問題困擾了我好久。之前要開Line Points都是先切換成4G，等到要下載App或看影片再換回Wi-Fi。&lt;/p&gt;
&lt;p&gt;某天在PTT上看到有人&lt;a href="https://www.ptt.cc/bbs/Android/M.1529553241.A.5C3.html"&gt;Wi-Fi環境下用Line無法上傳圖片&lt;/a&gt;。鄉民建議改&lt;a href="https://zh.wikipedia.org/wiki/%E6%9C%80%E5%A4%A7%E4%BC%A0%E8%BE%93%E5%8D%95%E5%85%83"&gt;MTU&lt;/a&gt;。我參考&lt;a href="http://awei791129.pixnet.net/blog/post/58092525-%5B%E7%B6%B2%E8%B7%AF%E5%84%AA%E5%8C%96%5D-%E5%A6%82%E4%BD%95%E6%9C%80%E4%BD%B3%E5%8C%96-mtu-%E5%A4%A7%E5%B0%8F-for-mac-os-x-%E5%92%8C-w"&gt;這篇&lt;/a&gt;找出最佳MTU值，套用上去之後，macOS分享的Wi-Fi也可以正常使用Line Points了。解決了一個延宕多時的問題，何其暢快！&lt;/p&gt;
&lt;p&gt;PS. 何其XX是港劇&lt;a href="https://zh.wikipedia.org/wiki/%E5%85%AC%E4%B8%BB%E5%AB%81%E5%88%B0"&gt;公主嫁到&lt;/a&gt;中男主角金多祿的口頭禪之一。&lt;/p&gt;</content><category term="misc"/></entry><entry><title>macOS上df無法正確顯示中文</title><link href="https://chyen.cc/blog/posts/2018/06/23/mac-df-chinese.html" rel="alternate"/><published>2018-06-23T22:29:00+08:00</published><updated>2018-06-23T22:29:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-06-23:/blog/posts/2018/06/23/mac-df-chinese.html</id><summary type="html">&lt;p&gt;最近心血來潮，把工作環境設定為中文。在Linux下是開一個檔案&lt;code&gt;~/.config/locale.conf&lt;/code&gt;，寫入&lt;code&gt;LANG=zh_TW.UTF-8&lt;/code&gt;。macOS下，iTerm2會自動設定&lt;code&gt;LANG&lt;/code&gt;和&lt;code&gt;LC_CTYPE&lt;/code&gt;這兩個環境變數。&lt;/p&gt;
&lt;p&gt;第一個出現問題的是zsh下，ssh的hostname自動完成。按 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;最近心血來潮，把工作環境設定為中文。在Linux下是開一個檔案&lt;code&gt;~/.config/locale.conf&lt;/code&gt;，寫入&lt;code&gt;LANG=zh_TW.UTF-8&lt;/code&gt;。macOS下，iTerm2會自動設定&lt;code&gt;LANG&lt;/code&gt;和&lt;code&gt;LC_CTYPE&lt;/code&gt;這兩個環境變數。&lt;/p&gt;
&lt;p&gt;第一個出現問題的是zsh下，ssh的hostname自動完成。按下tab後，游標會自動往前一格。目前還沒查出問題來。&lt;/p&gt;
&lt;p&gt;第二個問題是macOS下的df中文顯示錯亂。由於GNU coreutils整體上比mac內建的指令好用，因此我長久以來都是用MacPorts安裝的coreutils。&lt;/p&gt;
&lt;p&gt;今天環境改成中文時，&lt;code&gt;df&lt;/code&gt;出現怪異的輸出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;�?�?系統 容�?? 已�?� �?��?� 已�?�% �??�?�?
/dev/disk1s1    234G  151G    81G    65% /
/dev/disk1s4    234G  2.1G    81G     3% /private/var/vm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;這串文字丟進python encode後的結果是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\xe6\xaa&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xe6\xa1&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xe7\xb3\xbb\xe7\xb5\xb1&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\xae\xb9\xe9&lt;/span&gt;&lt;span class="s1"&gt;?? &lt;/span&gt;&lt;span class="se"&gt;\xe5\xb7\xb2\xe7&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xa8&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xaf\xe7&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xa8&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\xb7\xb2\xe7&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xa8&lt;/span&gt;&lt;span class="s1"&gt;% &lt;/span&gt;&lt;span class="se"&gt;\xe6&lt;/span&gt;&lt;span class="s1"&gt;??&lt;/span&gt;&lt;span class="se"&gt;\xe8\xbc&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="se"&gt;\xe9\xbb&lt;/span&gt;&lt;span class="s1"&gt;?&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;正確版本應該是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\xe6\xaa\x94\xe6\xa1\x88\xe7\xb3\xbb\xe7\xb5\xb1&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\xae\xb9\xe9\x87\x8f&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\xb7\xb2\xe7\x94\xa8&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\x8f\xaf\xe7\x94\xa8&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xe5\xb7\xb2\xe7\x94\xa8&lt;/span&gt;&lt;span class="s1"&gt;% &lt;/span&gt;&lt;span class="se"&gt;\xe6\x8e\x9b\xe8\xbc\x89\xe9\xbb\x9e&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;可以看出，某些byte被換成問號了。&lt;/p&gt;
&lt;p&gt;Google了一番，沒啥結果。決定自行把coreutils抓下來compile，再丟到lldb debug。一番努力之後，發現df的&lt;code&gt;hide_problematic_chars()&lt;/code&gt;會把iscntrl()回傳1的字元換成問號。這個函數在Linux上是正常的，但在mac上，卻會受到locale影響。我寫了一個小程式測試：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;locale.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;ctype.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iscntrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;setlocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LC_ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iscntrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;在Linux上，輸出0 0，而在mac上輸出0 1。&lt;/p&gt;
&lt;p&gt;目前先自行編一個&lt;code&gt;hide_problematic_chars()&lt;/code&gt;改過的版本將就用著，等有空再向GNU提bug。&lt;/p&gt;
&lt;p&gt;2020/11/08更新：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;向coreutils提交了一份&lt;a href="https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32236"&gt;bug report&lt;/a&gt;，接著我的patch被修改後&lt;a href="https://github.com/coreutils/coreutils/commit/437555061ca94fb009cdf0e96b3368f00de95f1e"&gt;commit&lt;/a&gt;到coreutils了！&lt;/li&gt;
&lt;li&gt;zsh中ssh tab補全似乎已經修好了。&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/></entry><entry><title>網站搬家</title><link href="https://chyen.cc/blog/posts/2018/06/01/domain-migraion.html" rel="alternate"/><published>2018-06-01T11:59:00+08:00</published><updated>2018-06-01T11:59:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-06-01:/blog/posts/2018/06/01/domain-migraion.html</id><summary type="html">&lt;p&gt;今天收到Let's Encrypt的通知信，說chyen.cc和*.chyen.cc的SSL憑證20天後過期。我這個網域名稱是在Namesilo買的。由於Let's Encrypt的wildcard certificate需要用DNS認證，然而certbot&lt;a href="https://github.com/certbot/certbot/issues/4595"&gt;還不支援Namesilo&lt;/a&gt;，所以我上次是手動把TXT紀錄一個一個(註：每個domain name會對應一個DNS challenge …&lt;/p&gt;</summary><content type="html">&lt;p&gt;今天收到Let's Encrypt的通知信，說chyen.cc和*.chyen.cc的SSL憑證20天後過期。我這個網域名稱是在Namesilo買的。由於Let's Encrypt的wildcard certificate需要用DNS認證，然而certbot&lt;a href="https://github.com/certbot/certbot/issues/4595"&gt;還不支援Namesilo&lt;/a&gt;，所以我上次是手動把TXT紀錄一個一個(註：每個domain name會對應一個DNS challenge)填上去。這次決定把DNS改由CloudFlare代管，用certbot的dns-cloudflare外掛直接取得新的SSL憑證。&lt;/p&gt;
&lt;p&gt;會選CloudFlare一個原因是我有考慮未來我的&lt;a href="https://dl.chyen.cc/arch-repo/"&gt;Arch Linux個人repo&lt;/a&gt;可能會需要開CDN，先把DNS搬過去之後就省的再搬。&lt;/p&gt;
&lt;p&gt;CloudFlare的教學我是參考&lt;a href="https://sofree.cc/cloudflare-free-cdn/"&gt;香腸大的文章&lt;/a&gt;，基本上設定滿簡單易懂的，新增DNS record後，propagate的時間也很短，大約幾秒到幾十秒而已，比Namesilo弄每個record都讓我等了好幾分鐘好多了。一個小小的缺點是，CloudFlare設定兩階段驗證只給QR code，不提供直接給TOTP secret的選項。為了把TOTP存進KeePassXC資料庫中，只好用&lt;a href="https://github.com/procxx/zbar"&gt;zbar&lt;/a&gt;把QR code還原回TOTP URI。&lt;/p&gt;
&lt;p&gt;接著用certbot請求一個新的SSL憑證。第一次遇到" The currently selected ACME CA endpoint does not support issuing wildcard certificates."的錯誤。原來是因為技術問題，certbot&lt;a href="https://github.com/certbot/certbot/issues/5369"&gt;到現在還沒預設為ACMEv2&lt;/a&gt;。這讓我想起了當初ACMEv2&lt;a href="https://community.letsencrypt.org/t/acmev2-and-wildcard-launch-delay/53654"&gt;因為安全性問題延後發布&lt;/a&gt;。ACMEv2還真是命運多舛啊！&lt;/p&gt;
&lt;p&gt;發好新憑證之後，順便把一些原本跑在chyen.csie.org的服務改到chyen.cc上。&lt;/p&gt;
&lt;p&gt;第一個是Quassel IRC server。把&lt;a href="https://github.com/certbot/certbot/issues/1250"&gt;新憑證複製過去&lt;/a&gt;，Quassel重新啟動就完成了，沒遇到什麼問題。&lt;/p&gt;
&lt;p&gt;接下來是Postfix mail server。我把main.cf改好，去Nextcloud, BuildBot, TT-RSS等地方把寄件者的domain name改成新的，以為這樣就完工了，實際測試寄信的時候卻跳出cannot connect的錯誤。這時我才想起我防火牆沒開Postfix，因此需要到/etc/hosts把"127.0.0.1 chyen.cc"加進去。&lt;/p&gt;
&lt;p&gt;這時候，寄信到gmail沒問題，寄到hotmail卻被當成垃圾信。查了一下發現DNS反解忘了改。到Linode把DNS反解也改成新的網址之後，hotmail還是不認我的信。目前可能只能先用白名單擋著了。&lt;/p&gt;
&lt;p&gt;最後是這個部落格。改一下pelican跟Apache的設定檔就好。也順便&lt;a href="https://github.com/yan12125/blog/issues/1"&gt;加上了RSS feed&lt;/a&gt;並把預設template裡面沒用的指令刪掉。&lt;/p&gt;
&lt;p&gt;搞這些花了我一個晚上。運維真的是件頗麻煩的工作XD&lt;/p&gt;</content><category term="misc"/></entry><entry><title>在Firefox 59上使用郵局的WebATM</title><link href="https://chyen.cc/blog/posts/2018/03/27/post-webatm-firefox.html" rel="alternate"/><published>2018-03-27T20:13:00+08:00</published><updated>2018-03-27T20:13:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-03-27:/blog/posts/2018/03/27/post-webatm-firefox.html</id><content type="html">&lt;hr&gt;
&lt;ol&gt;
&lt;li&gt;安裝郵局提供給Firefox/Chrome/Edge用的元件&lt;/li&gt;
&lt;li&gt;啟動元件&lt;/li&gt;
&lt;li&gt;匯入根憑證cacert.pem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;進階玩法：VirtualBox NAT port forwarding&lt;/p&gt;
&lt;p&gt;小小抱怨：WebATM元件一開機就會啟動，也不能取消，微不便&lt;/p&gt;</content><category term="misc"/></entry><entry><title>解決scrcpy crash的問題</title><link href="https://chyen.cc/blog/posts/2018/03/27/scrcpy-crash.html" rel="alternate"/><published>2018-03-27T20:10:00+08:00</published><updated>2018-03-27T20:10:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-03-27:/blog/posts/2018/03/27/scrcpy-crash.html</id><summary type="html">&lt;p&gt;之前聽說scrcpy可以把Android的螢幕同步到電腦上。第一次裝沒跑成功，crash了。&lt;/p&gt;
&lt;p&gt;經過一番測試，發現似乎SDL(scrcpy用的GUI library)會嘗試載入ibus，而強制指定&lt;code&gt;export SDL_IM_MODULE=fcitx&lt;/code&gt;後可以正常執行scrcpy。正 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;之前聽說scrcpy可以把Android的螢幕同步到電腦上。第一次裝沒跑成功，crash了。&lt;/p&gt;
&lt;p&gt;經過一番測試，發現似乎SDL(scrcpy用的GUI library)會嘗試載入ibus，而強制指定&lt;code&gt;export SDL_IM_MODULE=fcitx&lt;/code&gt;後可以正常執行scrcpy。正當準備提bug時，Arch Linux的sdl2來了一份更新，此後不需要&lt;code&gt;SDL_IM_MODULE&lt;/code&gt;也不會crash了。&lt;/p&gt;
&lt;p&gt;不知是否相關？聊表紀錄。&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Firefox Sync無法同步的問題</title><link href="https://chyen.cc/blog/posts/2018/03/16/firefox-nocertdb.html" rel="alternate"/><published>2018-03-16T21:17:00+08:00</published><updated>2018-03-16T21:17:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-03-16:/blog/posts/2018/03/16/firefox-nocertdb.html</id><summary type="html">&lt;p&gt;TL;DR security.nocertdb設定為true會讓Firefox Sync無法運作，改回false即可&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;最近發現我的Firefox Sync無法正常運作，在一台電腦上新加的addon沒有同步到其他電腦上。&lt;/p&gt;
&lt;p&gt;about:sync-log中的紀錄檔有以下錯誤訊息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;1520939110858&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;ged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;verify&lt;/span&gt;&lt;span class="n"&gt;ing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrowserIDManager&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;unlockAndVerifyAuthState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;declined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unlock …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;TL;DR security.nocertdb設定為true會讓Firefox Sync無法運作，改回false即可&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;最近發現我的Firefox Sync無法正常運作，在一台電腦上新加的addon沒有同步到其他電腦上。&lt;/p&gt;
&lt;p&gt;about:sync-log中的紀錄檔有以下錯誤訊息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;1520939110858&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;ged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;verify&lt;/span&gt;&lt;span class="n"&gt;ing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrowserIDManager&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;unlockAndVerifyAuthState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;declined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;master_password_locked&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Fetching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unlocked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="n"&gt;ed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;master_password_locked&lt;/span&gt;
&lt;span class="mf"&gt;1520939110862&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorHandler&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Sync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;encountered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;但是我並沒有設定master password。如果試著去建立一個master password，則會有"unable to change master password"的訊息。&lt;/p&gt;
&lt;p&gt;由於我曾經在Nightly、Beta、Stable之間切換，因此我猜可能是改版造成的問題。然而，試了各種版本後，問題依舊。&lt;/p&gt;
&lt;p&gt;此時，我懷疑可能我的profile有損毀的情況。官方說法是如果把Firefox降版，而profile沿用，有可能會發生。之前研究過Firefox Sync，他需要&lt;a href="https://github.com/pfn/passifox/issues/435#issuecomment-278680573"&gt;把登入用的一些資訊存在Firefox的password storage中&lt;/a&gt;。Profile損毀的話的確有可能會無法sync。&lt;/p&gt;
&lt;p&gt;然而，用&lt;a href="https://github.com/lclevy/firepwd"&gt;firepwd&lt;/a&gt;解密我的profile，又可以正常印出Firefox Sync登入用的資訊，所以問題可能出在Firefox本身。&lt;/p&gt;
&lt;p&gt;首先，將signon.debug設為true，可以多印出一些訊息。從訊息中可以得知logins.json中的username和password解密失敗。&lt;/p&gt;
&lt;p&gt;根據&lt;a href="https://github.com/lclevy/firepwd/blob/master/mozilla_pbe.pdf"&gt;firepwd提供的Firefox密碼解密原理說明&lt;/a&gt;，解密需要一把3DES的key，而這把key會用一組key ID來操作。&lt;/p&gt;
&lt;p&gt;我自行編譯&lt;a href="https://hg.mozilla.org/mozilla-central/"&gt;mozilla-central&lt;/a&gt;，再gdb後，發現Firefox可以解開所需的key ID，但找不到所對應的key。&lt;/p&gt;
&lt;p&gt;根據firepwd，3DES key是存在key3.db或key4.db中，然而讀取這兩種db的函數都沒有被執行到，表示key3.db或key4.db都沒有被讀進來。strace也給出相同的結論。&lt;/p&gt;
&lt;p&gt;接著發現在initialize NSS module時，configdir為空，因此NSS不會去找key3.db或是key4.db。我原本以為profile裡面設定錯誤，但看pkcs11.txt和secmod.db似乎都沒問題。&lt;/p&gt;
&lt;p&gt;最後發現如果security.nocertdb為true，就會把config設為空，因此整個解密的過程都無法進行。&lt;/p&gt;
&lt;p&gt;經過一番回想，當初設定這個參數是為了讓Firefox不要保留SSL immediate certificate，沒想到這個參數還有副作用。我猜他的本意是：不要讀取或寫入任何在permanent storage上和密碼以及安全有關的資料。沒了immediate certificate，也沒了password storage。&lt;/p&gt;
&lt;p&gt;現在只好先改回來，等哪天發現有更好的辦法可以disable immediate certificate，不然一大堆immediate certificate列在那邊實在很礙眼。&lt;/p&gt;</content><category term="misc"/></entry><entry><title>解決Arch Linux下emoji的顯示</title><link href="https://chyen.cc/blog/posts/2018/01/22/arch-linux-emoji.html" rel="alternate"/><published>2018-01-22T14:46:00+08:00</published><updated>2018-01-22T14:46:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-01-22:/blog/posts/2018/01/22/arch-linux-emoji.html</id><summary type="html">&lt;p&gt;KeePassXC&lt;a href="https://github.com/keepassxreboot/keepassxc/pull/1305"&gt;在entry view裡加了不少欄位&lt;/a&gt;，其中一個是&lt;a href="https://emojipedia.org/paperclip/"&gt;paperclip (📎, U+1F4CE)&lt;/a&gt;。在macOS正常，但在Arch Linux無法正常顯示，只有一個方框。裝了&lt;a href="https://www.archlinux.org/packages/extra/any/noto-fonts-emoji/"&gt;noto-fonts-emoji&lt;/a&gt;之後就正常了。&lt;/p&gt;
&lt;p&gt;根據&lt;a href="https://wiki.archlinux.org/index.php/Fonts_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#X11.E4.B8.AD.E7.9A.84.E5.AD.97.E4.BD.93.E5.9B.9E.E6.BB.9A.E9.A1.BA.E5.BA.8F"&gt;Arch Linux Wiki&lt;/a&gt;的說明，fontconfig會自動選一個有emoji的字型，所以理論上系統只 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;KeePassXC&lt;a href="https://github.com/keepassxreboot/keepassxc/pull/1305"&gt;在entry view裡加了不少欄位&lt;/a&gt;，其中一個是&lt;a href="https://emojipedia.org/paperclip/"&gt;paperclip (📎, U+1F4CE)&lt;/a&gt;。在macOS正常，但在Arch Linux無法正常顯示，只有一個方框。裝了&lt;a href="https://www.archlinux.org/packages/extra/any/noto-fonts-emoji/"&gt;noto-fonts-emoji&lt;/a&gt;之後就正常了。&lt;/p&gt;
&lt;p&gt;根據&lt;a href="https://wiki.archlinux.org/index.php/Fonts_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#X11.E4.B8.AD.E7.9A.84.E5.AD.97.E4.BD.93.E5.9B.9E.E6.BB.9A.E9.A1.BA.E5.BA.8F"&gt;Arch Linux Wiki&lt;/a&gt;的說明，fontconfig會自動選一個有emoji的字型，所以理論上系統只要裝一個就好。有看到一個比較花俏的&lt;a href="https://github.com/eosrei/twemoji-color-font"&gt;emoji字型&lt;/a&gt;，改天有空再來玩玩。&lt;/p&gt;
&lt;p&gt;裝了noto-fonts-emoji之後，xfwm4也可以正常顯示emoji了。現在剩下的問題就是&lt;a href="https://github.com/lxde/qtermwidget/pull/96"&gt;QTerminal/Konsole無法處理UCS-4&lt;/a&gt;...&lt;/p&gt;</content><category term="misc"/></entry><entry><title>AUR cleanup</title><link href="https://chyen.cc/blog/posts/2018/01/12/aur-cleanup.html" rel="alternate"/><published>2018-01-12T21:51:00+08:00</published><updated>2018-01-12T21:51:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2018-01-12:/blog/posts/2018/01/12/aur-cleanup.html</id><summary type="html">&lt;p&gt;今天把&lt;a href="https://github.com/yan12125/aur"&gt;yan12125/aur&lt;/a&gt;裡已經disowned的package都刪掉了。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/yan12125/aur/commit/47252d3d8020c1011068587355270d9ae62b9db5"&gt;yan12125/aur@47252d3d8020c1011068587355270d9ae62b9db5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;有種斷捨離的感覺...&lt;/p&gt;
&lt;p&gt;有些曾經是我追尋的目標，有則完全想不起來當初是用來做什麼的。無論 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;今天把&lt;a href="https://github.com/yan12125/aur"&gt;yan12125/aur&lt;/a&gt;裡已經disowned的package都刪掉了。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/yan12125/aur/commit/47252d3d8020c1011068587355270d9ae62b9db5"&gt;yan12125/aur@47252d3d8020c1011068587355270d9ae62b9db5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;有種斷捨離的感覺...&lt;/p&gt;
&lt;p&gt;有些曾經是我追尋的目標，有則完全想不起來當初是用來做什麼的。無論如何，過去
的，總是該過去了。&lt;/p&gt;
&lt;p&gt;一些印象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;boringssl-aur：在搞aur自動上傳的時候加的。&lt;a href="https://github.com/libgit2/pygit2/issues/552"&gt;pygit2&lt;/a&gt; -&amp;gt; libgit -&amp;gt; &lt;a href="https://github.com/libssh2/libssh2/issues/39#issuecomment-285889417"&gt;libssh2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;firefox-adblock-plus-hg：當初好像adblock plus有一些bug，只有在hg上的版本是正常運作的。現在我跳槽到ublock origin，而Firefox預設也不能安裝沒有數位簽章的外掛了。&lt;/li&gt;
&lt;li&gt;flashplugin-beta：看到Phoronix上&lt;a href="https://www.phoronix.com/scan.php?page=news_item&amp;amp;px=Adobe-Will-Update-Linux-Flash"&gt;Adobe Flash要重新支援Linux的新聞&lt;/a&gt;。有些網站在舊的Flash (11.x)上不執行，就去打包這個。在這之前，用&lt;a href="https://github.com/i-rinat/freshplayerplugin"&gt;PPAPI轉NPAPI的外掛&lt;/a&gt;用了一段時間，常常會有不穩定的情況。&lt;/li&gt;
&lt;li&gt;gcc46-multilib：印象中是舊版的是AOSP或Firefox OS用到的。&lt;/li&gt;
&lt;li&gt;js45：很多project (e.g. &lt;a href="https://github.com/libproxy/libproxy/issues/32"&gt;libproxy&lt;/a&gt;)用了舊版的spidermonkey，我試著porting了一些，都沒有upstream。中間送了不少build system patch給mozilla。後來gjs用到了js45，他就進官方repo了。&lt;/li&gt;
&lt;li&gt;k-map：大二修交換電路的時候打包的，算是我最早的幾個package之一。原來我用了Arch之後不久就開始打包package了。用Ubuntu一年多都沒打包過，Arch真的會改變一個人的思維。&lt;/li&gt;
&lt;li&gt;lxqt-panel-git：在還沒成為lxqt member時，曾短暫維護這個package一陣子。&lt;/li&gt;
&lt;li&gt;megatools-dev-git：本以為會是下一版megatools，想不到舊的branch更新還比較多。&lt;/li&gt;
&lt;li&gt;mips-mentor-linux-gnu：似乎是計算機結構的final project&lt;a href="https://github.com/libproxy/libproxy/issues/32"&gt;跑一個simulator&lt;/a&gt;用的。&lt;/li&gt;
&lt;li&gt;purple-line-git：本來希望可以在Linux上用LINE，結果把LINE帳號搞的亂七八糟的。也不支援貼圖。&lt;/li&gt;
&lt;li&gt;pyalpm-git：pyalpm有些bug的時候維護的，似乎是和新版Python不相容&lt;/li&gt;
&lt;li&gt;pypy3-hg：在pypy3還沒到3.5-compatible的時候用的&lt;/li&gt;
&lt;li&gt;python-pysvn：一個難搞的package，C++有太多陷阱&lt;/li&gt;
&lt;li&gt;tamarin-redux-hg：以前用來研究ActionScript的，後來Flash慢慢消失了&lt;/li&gt;
&lt;li&gt;xx-net：曾經想要做成package，就不用常常從git抓，然而XX-Net的設計理念不適合做成package&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這裡面js45 (libproxy), pypy3-hg, tamarin-redux-hg, xx-net多少和youtube-dl有關係。想想從youtube-dl出來的side project還真不少。後來有些變成單獨的project了，例如python3-android。研究VPN, proxy等等的過程中，也了解的不少中國網路的困境，進而對中國政治產生一些興趣。&lt;/p&gt;
&lt;p&gt;順帶一提，youtube-dl一開始是為了讓女友可以&lt;a href="https://github.com/rg3/youtube-dl/pull/4758"&gt;載streetvoice上的音樂&lt;/a&gt;才認真去研究的。說來可以算是從小打小鬧進入嚴肅的open source世界的濫觴。有空再來寫單獨一篇XD&lt;/p&gt;</content><category term="misc"/></entry><entry><title>解決郵局webatm「請檢查晶片是否已依正確方向插入讀卡機」的問題</title><link href="https://chyen.cc/blog/posts/2017/11/02/webatm.html" rel="alternate"/><published>2017-11-02T23:47:00+08:00</published><updated>2017-11-02T23:47:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-11-02:/blog/posts/2017/11/02/webatm.html</id><summary type="html">&lt;p&gt;郵局的webatm只要一台USB讀卡機和&lt;del&gt;萬惡的&lt;/del&gt;IE，就可以在家裡轉帳、查交易紀錄等等，非常方便。&lt;/p&gt;
&lt;p&gt;之前我都是用&lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;開一個Win7的VM，再用USB轉接功能讓裡面的Win7可以使用讀卡機。在Linux host跟Mac host用過，都沒啥問題 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;郵局的webatm只要一台USB讀卡機和&lt;del&gt;萬惡的&lt;/del&gt;IE，就可以在家裡轉帳、查交易紀錄等等，非常方便。&lt;/p&gt;
&lt;p&gt;之前我都是用&lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;開一個Win7的VM，再用USB轉接功能讓裡面的Win7可以使用讀卡機。在Linux host跟Mac host用過，都沒啥問題。&lt;/p&gt;
&lt;p&gt;然而，前幾天要轉帳的時候，照著往常的步驟，卻一直跳出「請檢查晶片是否已依正確方向插入讀卡機」的錯誤訊息。查了不少資料，試過不少方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://eric1774.pixnet.net/blog/post/28189027-%E4%B8%AD%E8%8F%AF%E9%83%B5%E6%94%BFatm-%E9%8C%AF%E8%AA%A4%E8%A8%8A%E6%81%AF-%22%E8%AB%8B%E6%AA%A2%E6%9F%A5%E6%99%B6%E7%89%87%E6%98%AF%E5%90%A6%E5%B7%B2%E4%BE%9D%E6%AD%A3"&gt;將Certificate Propagation服務停用&lt;/a&gt;。仍舊出現錯誤&lt;/li&gt;
&lt;li&gt;&lt;a href="http://eric1774.pixnet.net/blog/post/28189027-%E4%B8%AD%E8%8F%AF%E9%83%B5%E6%94%BFatm-%E9%8C%AF%E8%AA%A4%E8%A8%8A%E6%81%AF-%22%E8%AB%8B%E6%AA%A2%E6%9F%A5%E6%99%B6%E7%89%87%E6%98%AF%E5%90%A6%E5%B7%B2%E4%BE%9D%E6%AD%A3"&gt;將Smart Card停用再啟用&lt;/a&gt;，還是無效&lt;/li&gt;
&lt;li&gt;&lt;a href="http://web.archive.org/web/20130419043927/https://www.ptt.cc/bbs/Bank_Service/M.1357568053.A.D58.html"&gt;把卡片用力壓&lt;/a&gt;，沒用&lt;/li&gt;
&lt;li&gt;用橡皮擦把晶片的地方擦一擦，沒用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;查了那麼多資料，我都覺得好像在到處亂找偏方XD&lt;/p&gt;
&lt;p&gt;後來想想，可能是VirtualBox的問題。找了一台有裝Windows的電腦，一插上去，還真的可以用。&lt;/p&gt;
&lt;p&gt;檢查了一下VirtualBox的設定，發現沒有裝VirtualBox Extension Pack。下載最新版的回來安裝，再把USB設定成USB 2.0就OK了。&lt;/p&gt;
&lt;p&gt;我以前以為Extension Pack是&lt;a href="https://www.virtualbox.org/wiki/Editions"&gt;OSE&lt;/a&gt;版的才要裝，想不到&lt;a href="https://www.virtualbox.org/wiki/Editions"&gt;PUEL&lt;/a&gt;的也要囧&lt;/p&gt;
&lt;p&gt;BTW，想不到我竟然會有一天需要用&lt;a href="https://web.archive.org/"&gt;Wayback Machine&lt;/a&gt;來放PTT的連結...希望PTT趕快好起來啊。&lt;a href="https://zh.wikipedia.org/wiki/%E6%89%B9%E8%B8%A2%E8%B8%A2#.E8.A8.98.E9.8C.84"&gt;PTT 2017/10/30 故障事件&lt;/a&gt;&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Revert Revert Revert Revert</title><link href="https://chyen.cc/blog/posts/2017/10/25/revert-revert-revert-revert.html" rel="alternate"/><published>2017-10-25T11:53:00+08:00</published><updated>2017-10-25T11:53:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-10-25:/blog/posts/2017/10/25/revert-revert-revert-revert.html</id><content type="html">&lt;p&gt;在AOSP的Gerrit上看到一個commit message很有趣的&lt;a href="https://android-review.googlesource.com/c/platform/bionic/+/519835"&gt;review request&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;這樣做的好像還不少，從&lt;a href="https://zachholman.com/posts/searching-github-issues/"&gt;Github的搜尋&lt;/a&gt;可以略知一二XD&lt;/p&gt;</content><category term="misc"/></entry><entry><title>安裝 Pelican</title><link href="https://chyen.cc/blog/posts/2017/10/15/installing-pelican.html" rel="alternate"/><published>2017-10-15T01:31:00+08:00</published><updated>2017-10-15T01:31:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-10-15:/blog/posts/2017/10/15/installing-pelican.html</id><summary type="html">&lt;p&gt;來玩玩自己的部落格～把pelican裝起來，寫篇測試文章&lt;/p&gt;
&lt;p&gt;TODO:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy - 用git hook似乎是個選項&lt;/li&gt;
&lt;li&gt;Themes - http://www.pelicanthemes.com/上面滿多選擇的&lt;/li&gt;
&lt;li&gt;Git設定 - &lt;code&gt;.gitignore&lt;/code&gt;, 放到Github等等&lt;/li&gt;
&lt;li&gt;設定文章網址，讓他裡面有 …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;來玩玩自己的部落格～把pelican裝起來，寫篇測試文章&lt;/p&gt;
&lt;p&gt;TODO:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy - 用git hook似乎是個選項&lt;/li&gt;
&lt;li&gt;Themes - http://www.pelicanthemes.com/上面滿多選擇的&lt;/li&gt;
&lt;li&gt;Git設定 - &lt;code&gt;.gitignore&lt;/code&gt;, 放到Github等等&lt;/li&gt;
&lt;li&gt;設定文章網址，讓他裡面有日期&lt;/li&gt;
&lt;li&gt;字型 - 標題預設的中文字型頗怪&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可能會做的事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;留言系統 - farseerfc分享了一個相當有趣的點子：&lt;a href="https://farseerfc.me/github-issues-as-comments.html"&gt;使用Github issues當作留言系統&lt;/a&gt;。不過我還是別用那麼geek的系統好了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;UPDATE 2017/10/23:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy懶得研究那麼多了XD 直接在server上clone一份，Apache的設定檔指過去，搞定收工！&lt;/li&gt;
&lt;li&gt;先放到我自己的gogs server，有空再來放github&lt;/li&gt;
&lt;li&gt;用&lt;code&gt;ARTICLE_URL&lt;/code&gt;和&lt;code&gt;PAGE_URL&lt;/code&gt;設定好了有日期的網址。&lt;a href="http://docs.getpelican.com/en/3.6.3/settings.html#url-settings"&gt;官方教學&lt;/a&gt;用&lt;code&gt;*_SAVE_AS&lt;/code&gt;來達到沒有&lt;code&gt;.html&lt;/code&gt;的效果。我個人是覺得直接用&lt;code&gt;xxx.html&lt;/code&gt;就好啦。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;幾個小抱怨點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;預設提供的Makefile不會先clean再輸出，沒注意的話改網址的時候會留下沒用的檔案&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ARTICLE_URL&lt;/code&gt;和&lt;code&gt;ARTICAL_URL_SAVE_AS&lt;/code&gt;必須兩個同時設定，否則文章會連不到，不太直覺。&lt;/li&gt;
&lt;/ol&gt;</content><category term="misc"/></entry><entry><title>有趣的書名</title><link href="https://chyen.cc/blog/posts/2017/09/20/fun-book.html" rel="alternate"/><published>2017-09-20T23:54:00+08:00</published><updated>2017-09-20T23:54:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-09-20:/blog/posts/2017/09/20/fun-book.html</id><summary type="html">&lt;p&gt;之前有出現過一波&lt;a href="http://www.jianshu.com/p/f9e1b928e3fe"&gt;「從入門到XX」系列的惡搞書名&lt;/a&gt;，例如「PHP: 從入門到出軌」、「C語言：從入門到放棄」等等。這些都是惡搞合成的圖片。不 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;之前有出現過一波&lt;a href="http://www.jianshu.com/p/f9e1b928e3fe"&gt;「從入門到XX」系列的惡搞書名&lt;/a&gt;，例如「PHP: 從入門到出軌」、「C語言：從入門到放棄」等等。這些都是惡搞合成的圖片。不過最近看到有人真用這種句型寫書，標題就叫「&lt;a href="https://shazi7804.gitbooks.io/puppet-manage-guide/content/"&gt;Puppet：從入門就放棄&lt;/a&gt;」，不過內容倒是一本正經XD&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Mac shortcuts are broken with UTF-8</title><link href="https://chyen.cc/blog/posts/2017/09/16/mac-shortcuts-broken-with-utf8.html" rel="alternate"/><published>2017-09-16T12:51:00+08:00</published><updated>2017-09-16T12:51:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-09-16:/blog/posts/2017/09/16/mac-shortcuts-broken-with-utf8.html</id><summary type="html">&lt;p&gt;I was trying to enable copying and pasting between Linux and macOS via SSH. I followed several guides (&lt;a href="http://brettterpstra.com/2014/02/19/remote-pbcopy-on-os-x-systems/"&gt;1&lt;/a&gt; &lt;a href="https://seancoates.com/blogs/remote-pbcopy/"&gt;2&lt;/a&gt; &lt;a href="https://gist.github.com/burke/5960455"&gt;3&lt;/a&gt;) and set up a local server to handle exchanged clipboard data. OK, things look good.&lt;/p&gt;
&lt;p&gt;However, soon I found that pasting CJK brings question marks. A &lt;a href="http://hints.macworld.com/article.php?story=20081231012753422"&gt;tip&lt;/a&gt; suggests setting …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I was trying to enable copying and pasting between Linux and macOS via SSH. I followed several guides (&lt;a href="http://brettterpstra.com/2014/02/19/remote-pbcopy-on-os-x-systems/"&gt;1&lt;/a&gt; &lt;a href="https://seancoates.com/blogs/remote-pbcopy/"&gt;2&lt;/a&gt; &lt;a href="https://gist.github.com/burke/5960455"&gt;3&lt;/a&gt;) and set up a local server to handle exchanged clipboard data. OK, things look good.&lt;/p&gt;
&lt;p&gt;However, soon I found that pasting CJK brings question marks. A &lt;a href="http://hints.macworld.com/article.php?story=20081231012753422"&gt;tip&lt;/a&gt; suggests setting &lt;code&gt;__CF_USER_TEXT_ENCODING&lt;/code&gt;, which doesn't work for me. I ended up changing the content of &lt;code&gt;~/.CFUserTextEncoding&lt;/code&gt; to &lt;code&gt;0x8000100:0x8000100&lt;/code&gt;. After reboot, copying and pasting CJK just works (TM).&lt;/p&gt;
&lt;p&gt;But something strange hits me. Quite a few shortcuts involving Command doesn't work in Firefox and KeePassXC. With some trial and error, it turns out modified &lt;code&gt;~/.CFUserTextEncoding&lt;/code&gt; broke things. I have no choice but changing it back.&lt;/p&gt;
&lt;p&gt;KeePassXC uses QShortcut and Firefox appears to use Cocoa directly. What stops them from working? Dunno.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/corefoundation/cfstringbuiltinencodings/1542603-utf8"&gt;0x8000100 is exactly UTF-8&lt;/a&gt;. So, another amazing story of macOS: using UTF-8 as the default encoding can break things!&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Network is down after logged out</title><link href="https://chyen.cc/blog/posts/2017/08/22/network-down-after-logout.html" rel="alternate"/><published>2017-08-22T11:20:00+08:00</published><updated>2017-08-22T11:20:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-08-22:/blog/posts/2017/08/22/network-down-after-logout.html</id><summary type="html">&lt;p&gt;Recently I got a mysterious bug. I have a server, and whenever I logouts all sessions, its network is broken. I can neither ping it nor ssh into it.&lt;/p&gt;
&lt;p&gt;The culprit is NetworkManager. "All users may connect to this network" is not checked, so only my account can access the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently I got a mysterious bug. I have a server, and whenever I logouts all sessions, its network is broken. I can neither ping it nor ssh into it.&lt;/p&gt;
&lt;p&gt;The culprit is NetworkManager. "All users may connect to this network" is not checked, so only my account can access the network.&lt;/p&gt;
&lt;p&gt;Reference: &lt;a href="https://www.centos.org/forums/viewtopic.php?t=49267"&gt;https://www.centos.org/forums/viewtopic.php?t=49267&lt;/a&gt;&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Select printer with lpr</title><link href="https://chyen.cc/blog/posts/2017/08/20/lpr-select-printer.html" rel="alternate"/><published>2017-08-20T00:00:00+08:00</published><updated>2017-08-20T00:00:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2017-08-20:/blog/posts/2017/08/20/lpr-select-printer.html</id><summary type="html">&lt;p&gt;On Arch Linux, I use the following command to print documents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;PRINTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;the_printer_name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lpr&lt;span class="w"&gt; &lt;/span&gt;~/my_doc.pdf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It was always working yet broken with the latest version. A mysterious error appears:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PRINTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;the_printer_name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lpr&lt;span class="w"&gt; &lt;/span&gt;~/my_doc.pdf
lpr:&lt;span class="w"&gt; &lt;/span&gt;Error&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;scheduler&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;responding.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By tracing the codes, seems &lt;code&gt;$PRINTER&lt;/code&gt; does not work. Now …&lt;/p&gt;</summary><content type="html">&lt;p&gt;On Arch Linux, I use the following command to print documents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;PRINTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;the_printer_name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lpr&lt;span class="w"&gt; &lt;/span&gt;~/my_doc.pdf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It was always working yet broken with the latest version. A mysterious error appears:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PRINTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;the_printer_name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lpr&lt;span class="w"&gt; &lt;/span&gt;~/my_doc.pdf
lpr:&lt;span class="w"&gt; &lt;/span&gt;Error&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;scheduler&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;responding.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By tracing the codes, seems &lt;code&gt;$PRINTER&lt;/code&gt; does not work. Now I have to use another pattern to specify the printer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;lpr&lt;span class="w"&gt; &lt;/span&gt;-P&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;the_printer_name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/my_doc.pdf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After debugging and tracing codes, it turns out that the bug is already &lt;a href="https://github.com/apple/cups/commit/c536b6c583abd9ea1b750f15e887b313ed7ad951"&gt;fixed&lt;/a&gt; upstream. I applied the package and rebuild CUPS. Now everything is fine :)&lt;/p&gt;</content><category term="misc"/></entry><entry><title>youtube-dl on Android</title><link href="https://chyen.cc/blog/posts/2015/11/22/youtube-dl-on-android.html" rel="alternate"/><published>2015-11-22T17:06:00+08:00</published><updated>2015-11-22T17:06:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-11-22:/blog/posts/2015/11/22/youtube-dl-on-android.html</id><summary type="html">&lt;p&gt;最近在研究如何把youtube-dl跑在Android上(&lt;a href="https://github.com/rg3/youtube-dl/issues/967"&gt;youtube-dl#967&lt;/a&gt;)。方法大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找一個可以跑的Python interpreter&lt;/li&gt;
&lt;li&gt;把&lt;a href="https://github.com/rg3/youtube-dl"&gt;youtube-dl&lt;/a&gt; clone下來或是從&lt;a href="https://yt-dl.org"&gt;yt-dl.org&lt;/a&gt;下載最新的tarball&lt;/li&gt;
&lt;li&gt;用adb shell或terminal emulater執行youtube-dl&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;針對第一步，Google Play上面很多個，例如 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;最近在研究如何把youtube-dl跑在Android上(&lt;a href="https://github.com/rg3/youtube-dl/issues/967"&gt;youtube-dl#967&lt;/a&gt;)。方法大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找一個可以跑的Python interpreter&lt;/li&gt;
&lt;li&gt;把&lt;a href="https://github.com/rg3/youtube-dl"&gt;youtube-dl&lt;/a&gt; clone下來或是從&lt;a href="https://yt-dl.org"&gt;yt-dl.org&lt;/a&gt;下載最新的tarball&lt;/li&gt;
&lt;li&gt;用adb shell或terminal emulater執行youtube-dl&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;針對第一步，Google Play上面很多個，例如&lt;a href="https://play.google.com/store/apps/details?id=org.kivy.pygame"&gt;Kivy Launcher&lt;/a&gt;，&lt;a href="https://play.google.com/store/apps/details?id=com.hipipal.qpy3"&gt;QPython3&lt;/a&gt;等等。我玩過之後都不太滿意。因為這些App要嘛我找不到一個單獨的python執行檔，要嘛是在app自己的資料夾下，shell讀不到。於是我決定用自己compile的版本。在網路上找"Python NDK"，找到了&lt;a href="https://github.com/rave-engine/python3-android/"&gt;python3-android&lt;/a&gt;這個project。照著說明下去跑，沒有compile error，看起來很棒。放到手機上，設定好&lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;就可以用，也不錯，不過一跑youtube-dl就出錯：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@GT-N7000:/data/local/tmp # python3 youtube_dl/__main__.py
Traceback (most recent call last):
  File &amp;quot;youtube_dl/__main__.py&amp;quot;, line 16, in &amp;lt;module&amp;gt;
    import youtube_dl
  File &amp;quot;/data/local/tmp/youtube_dl/__init__.py&amp;quot;, line 15, in &amp;lt;module&amp;gt;
    from .options import (
  File &amp;quot;/data/local/tmp/youtube_dl/options.py&amp;quot;, line 7, in &amp;lt;module&amp;gt;
    from .downloader.external import list_external_downloaders
  File &amp;quot;/data/local/tmp/youtube_dl/downloader/__init__.py&amp;quot;, line 3, in &amp;lt;module&amp;gt;
    from .common import FileDownloader
  File &amp;quot;/data/local/tmp/youtube_dl/downloader/common.py&amp;quot;, line 8, in &amp;lt;module&amp;gt;
    from ..compat import compat_str
  File &amp;quot;/data/local/tmp/youtube_dl/compat.py&amp;quot;, line 14, in &amp;lt;module&amp;gt;
    import subprocess
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/subprocess.py&amp;quot;, line 406, in &amp;lt;module&amp;gt;
    import select
ImportError: dlopen failed: cannot locate symbol &amp;quot;ceil&amp;quot; referenced by &amp;quot;select.cpython-34m.so&amp;quot;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;在另外一台手機HTC butterfly x920上error不太一樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;shell@android:/data/local/tmp $ python3 -c &amp;#39;import select&amp;#39;
Traceback (most recent call last):
  File &amp;quot;&amp;lt;string&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
ImportError: Cannot load library: reloc_library[1306]:  2142 cannot locate &amp;#39;ceil&amp;#39;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;在網路上找到&lt;a href="https://mail.python.org/pipermail/python-dev/2014-October/136715.html"&gt;這篇&lt;/a&gt;，他說select需要link &lt;code&gt;libm.so&lt;/code&gt;，我&lt;a href="https://github.com/yan12125/python3-android/commit/ee736929d455fe3ee3c75368d4c0129c66ccd6ca"&gt;照做&lt;/a&gt;了之後select就可以被import進來了。&lt;/p&gt;
&lt;p&gt;接下來是這個問題：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;shell@android:/ $ python3 -c &amp;#39;import platform; platform.platform()&amp;#39;
Traceback (most recent call last):
  File &amp;quot;&amp;lt;string&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/platform.py&amp;quot;, line 1470, in platform
    system, node, release, version, machine, processor = uname()
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/platform.py&amp;quot;, line 1151, in uname
    processor = _syscmd_uname(&amp;#39;-p&amp;#39;, &amp;#39;&amp;#39;)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/platform.py&amp;quot;, line 905, in _syscmd_uname
    f = os.popen(&amp;#39;uname %s 2&amp;gt; %s&amp;#39; % (option, DEV_NULL))
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/os.py&amp;quot;, line 943, in popen
    bufsize=buffering)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/subprocess.py&amp;quot;, line 859, in __init__
    restore_signals, start_new_session)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/subprocess.py&amp;quot;, line 1357, in _execute_child
    raise RuntimeError(&amp;#39;Could not find system shell&amp;#39;)
RuntimeError: Could not find system shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;看了下&lt;code&gt;subprocess.py&lt;/code&gt;，這段code是python3-android的patch：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;platform&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;android_version&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/system/bin/sh&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Could not find system shell&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;在蝴蝶機上&lt;code&gt;platform.android_version()&lt;/code&gt;給出的是&lt;code&gt;('', '')&lt;/code&gt;，於是我又看了下&lt;code&gt;android_version()&lt;/code&gt;的定義：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;output = subprocess.check_output([&amp;#39;/system/bin/getprop&amp;#39;,
                                  _android_version_property])
version = output.decode(&amp;#39;ascii&amp;#39;).strip()
version_obtained = True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;奇怪的是，我在shell裡直接打&lt;code&gt;/system/bin/getprop ro.build.version.release&lt;/code&gt;就會有正確的值4.1.1，用python下&lt;code&gt;subprocess.check_output(['/system/bin/getprop', 'ro.build.version.release'])&lt;/code&gt;或是&lt;code&gt;subprocess.check_output(['/system/bin/getprop'])&lt;/code&gt;都給我&lt;code&gt;b''&lt;/code&gt;。&lt;a href="http://benno.id.au/android/strace"&gt;strace&lt;/a&gt;一下也沒啥發現。只好來改falling back讀檔案的部份。&lt;/p&gt;
&lt;p&gt;不得不說這部份真是bug百出，原作者應該沒有好好的在各種手機上測過，最後改完大概像&lt;a href="https://github.com/yan12125/python3-android/commit/ed8b18b22c1cadf7e876fff4e4a29c78de9e50aa"&gt;這個樣子&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;這兩個地方我把改過的部份送回上游&lt;a href="https://github.com/rave-engine/python3-android/pull/10"&gt;python3-android#10&lt;/a&gt;，pull request裡也有說明我遇到的各種情況。&lt;/p&gt;
&lt;p&gt;現在大致上可以跑了，除了SSL &lt;code&gt;CERTIFICATE_VERIFY_FAILED&lt;/code&gt;之外：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;shell@android:/sdcard/Android/data/me.sheimi.sgit/files/repo/ytdl $ python3 -m youtube_dl -v cnFmi7AXeK8
[debug] System config: []
[debug] User config: []
[debug] Command-line args: [&amp;#39;-v&amp;#39;, &amp;#39;cnFmi7AXeK8&amp;#39;]
[debug] Encodings: locale ascii, fs utf-8, out ascii, pref ascii
[debug] youtube-dl version 2015.11.19
[debug] Python version 3.4.3 - Linux-3.4.10-ga9fe3b3-armv7l-with-libc
[debug] exe versions: none
[debug] Proxy map: {}
[youtube] cnFmi7AXeK8: Downloading webpage
ERROR: Unable to download webpage: &amp;lt;urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)&amp;gt; (caused by URLError(SSLError(1, &amp;#39;[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)&amp;#39;),))
    response = self._open(req, data)
  File &amp;quot;/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/extractor/common.py&amp;quot;, line 329, in _request_webpage
    return self._downloader.urlopen(url_or_request)
  File &amp;quot;/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/YoutubeDL.py&amp;quot;, line 1874, in urlopen
    return self._opener.open(req, timeout=self._socket_timeout)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/urllib/request.py&amp;quot;, line 463, in open
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/urllib/request.py&amp;quot;, line 481, in _open
    &amp;#39;_open&amp;#39;, req)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/urllib/request.py&amp;quot;, line 441, in _call_chain
    result = func(*args)
  File &amp;quot;/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/utils.py&amp;quot;, line 798, in https_open
    req, **kwargs)
  File &amp;quot;/data/local/tmp/python3/lib/python3.4/urllib/request.py&amp;quot;, line 1184, in do_open
    raise URLError(err)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;這是個老問題了&lt;a href="https://github.com/rg3/youtube-dl/search?utf8=%E2%9C%93&amp;amp;q=ssl+certificate_verify_failed&amp;amp;type=Issues"&gt;youtube-dl ssl certificate_verify_failed&lt;/a&gt;，一個常見的解法是設定&lt;code&gt;$SSL_CERT_DIR&lt;/code&gt;，但是我設了&lt;code&gt;/system/etc/security/cacerts&lt;/code&gt;還是失敗，strace一下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;stat64(&amp;quot;/system/etc/security/cacerts/578d5c04.0&amp;quot;, 0xbe99c130) = -1 ENOENT (No such file or directory)
stat64(&amp;quot;/system/etc/security/cacerts/2c543cd1.0&amp;quot;, 0xbe99c130) = -1 ENOENT (No such file or directory)
stat64(&amp;quot;/system/etc/security/cacerts/c4c7a654.0&amp;quot;, 0xbe99c130) = -1 ENOENT (No such file or directory)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Android的確沒有這三個檔案，而Arch Linux上有2c543cd1.0和578d5c04.0，莫非Android上的根憑證和一般人不一樣？&lt;/p&gt;
&lt;p&gt;EDIT 2019-09-21&lt;/p&gt;
&lt;p&gt;後來certificate的問題解決了。詳細可參考&lt;a href="https://github.com/yan12125/python3-android/#ssltls"&gt;python3-android SSL/TLS&lt;/a&gt;。&lt;/p&gt;
&lt;!--
# Below texts are from 2015. I don't remember what's the purpose now.
Reference
Colliding X.509 Certificates
http://eprint.iacr.org/2005/067.pdf
--&gt;</content><category term="misc"/></entry><entry><title>助教見聞錄 - 沒有回傳值的non-void函數</title><link href="https://chyen.cc/blog/posts/2015/11/18/non-void-function-without-return-values.html" rel="alternate"/><published>2015-11-18T02:09:08+08:00</published><updated>2015-11-18T02:09:08+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-11-18:/blog/posts/2015/11/18/non-void-function-without-return-values.html</id><summary type="html">&lt;p&gt;昨天有位小大一來問問題，他寫的code在local可以跑，但是上傳到Judgegirl都是wrong answer。&lt;/p&gt;
&lt;p&gt;我看了一下題目，是要用遞迴計算 &lt;span class="math"&gt;\(1^2 + 2^2 + \ldots + n^2\)&lt;/span&gt; 。他的code大概長這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;int …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;昨天有位小大一來問問題，他寫的code在local可以跑，但是上傳到Judgegirl都是wrong answer。&lt;/p&gt;
&lt;p&gt;我看了一下題目，是要用遞迴計算 &lt;span class="math"&gt;\(1^2 + 2^2 + \ldots + n^2\)&lt;/span&gt; 。他的code大概長這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;用gcc編譯：&lt;code&gt;gcc squares.c&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;執行結果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;./a.out
&lt;span class="m"&gt;55&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;看起來是對的，&lt;span class="math"&gt;\(1^2 + 2^2 + 3^2 + 4^2 + 5^2 = 55\)&lt;/span&gt;，為何judge不過？&lt;/p&gt;
&lt;p&gt;其實滿明顯的，第二個&lt;code&gt;return&lt;/code&gt;應該是&lt;code&gt;return t&lt;/code&gt;才對。&lt;/p&gt;
&lt;p&gt;問題來了，為何結果是對的？&lt;/p&gt;
&lt;p&gt;這得從x86 calling convention開始講起（&lt;a href="https://en.wikipedia.org/wiki/X86_calling_conventions"&gt;維基百科&lt;/a&gt;）。一般的x86程式中，函數執行到return時是把return value放進eax暫存器中，然後jump回去。看看這兩個函數的assembly即可見真章：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ gdb ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
(gdb) disassemble squares
Dump of assembler code for function squares:
   0x0000000000400506 &amp;lt;+0&amp;gt;: push   rbp
   0x0000000000400507 &amp;lt;+1&amp;gt;: mov    rbp,rsp
   0x000000000040050a &amp;lt;+4&amp;gt;: push   rbx
   0x000000000040050b &amp;lt;+5&amp;gt;: sub    rsp,0x28
   0x000000000040050f &amp;lt;+9&amp;gt;: mov    DWORD PTR [rbp-0x24],edi
   0x0000000000400512 &amp;lt;+12&amp;gt;:    mov    DWORD PTR [rbp-0x14],0x0
   0x0000000000400519 &amp;lt;+19&amp;gt;:    cmp    DWORD PTR [rbp-0x24],0x1
   0x000000000040051d &amp;lt;+23&amp;gt;:    jne    0x400528 &amp;lt;squares+34&amp;gt;
   0x000000000040051f &amp;lt;+25&amp;gt;:    add    DWORD PTR [rbp-0x14],0x1
   0x0000000000400523 &amp;lt;+29&amp;gt;:    mov    eax,DWORD PTR [rbp-0x14]
   0x0000000000400526 &amp;lt;+32&amp;gt;:    jmp    0x400543 &amp;lt;squares+61&amp;gt;
   0x0000000000400528 &amp;lt;+34&amp;gt;:    mov    eax,DWORD PTR [rbp-0x24]
   0x000000000040052b &amp;lt;+37&amp;gt;:    imul   eax,DWORD PTR [rbp-0x24]
   0x000000000040052f &amp;lt;+41&amp;gt;:    mov    ebx,eax
   0x0000000000400531 &amp;lt;+43&amp;gt;:    mov    eax,DWORD PTR [rbp-0x24]
   0x0000000000400534 &amp;lt;+46&amp;gt;:    sub    eax,0x1
   0x0000000000400537 &amp;lt;+49&amp;gt;:    mov    edi,eax
   0x0000000000400539 &amp;lt;+51&amp;gt;:    call   0x400506 &amp;lt;squares&amp;gt;
   0x000000000040053e &amp;lt;+56&amp;gt;:    add    eax,ebx
   0x0000000000400540 &amp;lt;+58&amp;gt;:    add    DWORD PTR [rbp-0x14],eax
   0x0000000000400543 &amp;lt;+61&amp;gt;:    add    rsp,0x28
   0x0000000000400547 &amp;lt;+65&amp;gt;:    pop    rbx
   0x0000000000400548 &amp;lt;+66&amp;gt;:    pop    rbp
   0x0000000000400549 &amp;lt;+67&amp;gt;:    ret
End of assembler dump.
(gdb) disassemble main
Dump of assembler code for function main:
   0x000000000040054a &amp;lt;+0&amp;gt;: push   rbp
   0x000000000040054b &amp;lt;+1&amp;gt;: mov    rbp,rsp
   0x000000000040054e &amp;lt;+4&amp;gt;: mov    edi,0x5
   0x0000000000400553 &amp;lt;+9&amp;gt;: call   0x400506 &amp;lt;squares&amp;gt;
   0x0000000000400558 &amp;lt;+14&amp;gt;:    mov    esi,eax
   0x000000000040055a &amp;lt;+16&amp;gt;:    mov    edi,0x4005f4
   0x000000000040055f &amp;lt;+21&amp;gt;:    mov    eax,0x0
   0x0000000000400564 &amp;lt;+26&amp;gt;:    call   0x4003e0 &amp;lt;printf@plt&amp;gt;
   0x0000000000400569 &amp;lt;+31&amp;gt;:    mov    eax,0x0
   0x000000000040056e &amp;lt;+36&amp;gt;:    pop    rbp
   0x000000000040056f &amp;lt;+37&amp;gt;:    ret
End of assembler dump.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;DWORD PTR [rbp-0x24]&lt;/code&gt;是&lt;code&gt;edi&lt;/code&gt;，也就是第一個function argument &lt;code&gt;rdi&lt;/code&gt;的lower bits，換句話說就是&lt;code&gt;n&lt;/code&gt;。squares+34把&lt;code&gt;n&lt;/code&gt;的值放到&lt;code&gt;eax&lt;/code&gt;，squares+41之後&lt;code&gt;ebx&lt;/code&gt;的值是&lt;code&gt;n * n&lt;/code&gt;。squares+43～squares+51把squares(n - 1)的值算出來放到&lt;code&gt;eax&lt;/code&gt;，squares+56之後&lt;code&gt;eax&lt;/code&gt;的值就是n * n + squares(n - 1)。這時候直接return答案剛好會是對的。&lt;/p&gt;
&lt;p&gt;就這樣，一連串的剛剛好，最後出來剛好結果是對的。然而judge上的compiler跟我們自己的compiler不一定一樣，這種undefined behavior，結果就很難說了。&lt;/p&gt;
&lt;p&gt;這樣的解釋，學弟好像聽不太懂，不過沒關係，岱神聽懂就好了^__^&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="misc"/></entry><entry><title>Installing Octopress</title><link href="https://chyen.cc/blog/posts/2015/11/17/installing-octopress.html" rel="alternate"/><published>2015-11-17T00:54:31+08:00</published><updated>2015-11-17T00:54:31+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-11-17:/blog/posts/2015/11/17/installing-octopress.html</id><summary type="html">&lt;p&gt;弄個部落格放一些技術文章是我一直想做的事。之前試過幾個平台，例如PTT，Wordpress等等，都覺得差強人意。主要是貼code很不方便。有一次看到有 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;弄個部落格放一些技術文章是我一直想做的事。之前試過幾個平台，例如PTT，Wordpress等等，都覺得差強人意。主要是貼code很不方便。有一次看到有人部落格用Octopress架站，好奇之下查了一些資料，發現他可以用Markdown寫文章，就下定決心要來試試看。&lt;/p&gt;
&lt;p&gt;我測試的環境是家裡電腦，系統是Arch Linux。折騰了兩三個小時，終於把Octopress裝起來了。過程中最大的問題是Octopress 3.x之後用的指令都跟2.x完全不同，最後用的指令如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;init
$&lt;span class="w"&gt; &lt;/span&gt;octopress&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;.
$&lt;span class="w"&gt; &lt;/span&gt;jekyll&lt;span class="w"&gt; &lt;/span&gt;build
$&lt;span class="w"&gt; &lt;/span&gt;octopress&lt;span class="w"&gt; &lt;/span&gt;deploy&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;rsync
$&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;_deploy.yml
$&lt;span class="w"&gt; &lt;/span&gt;octopress&lt;span class="w"&gt; &lt;/span&gt;deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;其中關鍵的地方是第二行&lt;code&gt;octopress new .&lt;/code&gt; 如果照著&lt;a href="https://github.com/octopress/octopress/blob/master/README.md#init"&gt;官方教學&lt;/a&gt;用&lt;code&gt;octopress init .&lt;/code&gt;，出來的HTML就沒有style。用以上步驟建出來的雖然不如&lt;a href="http://octopress.org/"&gt;官網&lt;/a&gt;的那麼漂亮，但也不錯看了。&lt;/p&gt;
&lt;p&gt;另外一個麻煩的地方是&lt;code&gt;_deploy.yml&lt;/code&gt;的設定。由於標準的rsync URI要有兩個冒號，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;yen@localhost::octopress/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;而Octopress的路徑是rsync over ssh，只有一個冒號，所以&lt;code&gt;_deploy.yml&lt;/code&gt;的&lt;code&gt;remote_path&lt;/code&gt;我另外加了一個冒號在前面。希望未來的版本能直接支援一般的rsync protocol。&lt;/p&gt;
&lt;p&gt;架好之後，用&lt;code&gt;jekyll serve -w&lt;/code&gt;就會在localhost:4000開一個server，markdown存檔時會自動重新build，相當方便。&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Welcome to Jekyll!</title><link href="https://chyen.cc/blog/posts/2015/11/16/welcome-to-jekyll.html" rel="alternate"/><published>2015-11-16T19:20:00+08:00</published><updated>2015-11-16T19:20:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-11-16:/blog/posts/2015/11/16/welcome-to-jekyll.html</id><summary type="html">&lt;p&gt;You’ll find this post in your &lt;code&gt;_posts&lt;/code&gt; directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run &lt;code&gt;jekyll serve&lt;/code&gt;, which launches a web server and auto-regenerates your site …&lt;/p&gt;</summary><content type="html">&lt;p&gt;You’ll find this post in your &lt;code&gt;_posts&lt;/code&gt; directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run &lt;code&gt;jekyll serve&lt;/code&gt;, which launches a web server and auto-regenerates your site when a file is updated.&lt;/p&gt;
&lt;p&gt;To add new posts, simply add a file in the &lt;code&gt;_posts&lt;/code&gt; directory that follows the convention &lt;code&gt;YYYY-MM-DD-name-of-post.ext&lt;/code&gt; and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.&lt;/p&gt;
&lt;p&gt;Jekyll also offers powerful support for code snippets:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;print_hi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hi, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;print_hi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Tom&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; prints &amp;#39;Hi, Tom&amp;#39; to STDOUT.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check out the &lt;a href="http://jekyllrb.com/docs/home"&gt;Jekyll docs&lt;/a&gt; for more info on how to get the most out of Jekyll. File all bugs/feature requests at &lt;a href="https://github.com/jekyll/jekyll"&gt;Jekyll’s GitHub repo&lt;/a&gt;. If you have questions, you can ask them on &lt;a href="https://talk.jekyllrb.com/"&gt;Jekyll Talk&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"/></entry><entry><title>Compiling ZTE OPEN kernel on Arch Linux</title><link href="https://chyen.cc/blog/posts/2015/08/12/compiling-zte-open-kernel-on-arch-linux.html" rel="alternate"/><published>2015-08-12T13:29:00+08:00</published><updated>2015-08-12T13:29:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-08-12:/blog/posts/2015/08/12/compiling-zte-open-kernel-on-arch-linux.html</id><summary type="html">&lt;p&gt;I need to modify kernel source codes of Firefox OS for my project.
Currently I have a ZTE OPEN device (NOTE: it's different from ZTE OPEN
C). ZTE have lots of kernel sources released to public, but this device
is not included. After several e-mails, ZTE engineers finally release
the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I need to modify kernel source codes of Firefox OS for my project.
Currently I have a ZTE OPEN device (NOTE: it's different from ZTE OPEN
C). ZTE have lots of kernel sources released to public, but this device
is not included. After several e-mails, ZTE engineers finally release
the kernel source for this device. Cheers!&lt;/p&gt;
&lt;p&gt;After several days of trials and errors, I finally compiled and
installed the kernel image. I use Arch Linux. This distribution features
all softwares being latest. Here's an incomplete list of my installed
softwares:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gcc-multilib 5.2.0-2&lt;/li&gt;
&lt;li&gt;perl 5.22.0-1&lt;/li&gt;
&lt;li&gt;python2 2.7.10-1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here's how I've compiled the kernel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First download the kernel source image from
    &lt;a href="http://opensource.ztedevice.com/"&gt;http://opensource.ztedevice.com/&lt;/a&gt;. Choose the "ZTE OPEN Icecream
    Sandwich Kernel(3.0.X)" one.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extract the kernel source:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;zxvf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ZTE_OPEN_Icecream_Sandwich_Kernel(3.0.X).tar.gz&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download arm-eabi-gcc from google:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go into the compiler and add the \$PATH&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pushd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;eabi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;4.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;$PATH&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n n-Quoted"&gt;`pwd`&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go into the kernel source and check arm-eabi-gcc works&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;arm-eabi-gcc&lt;span class="w"&gt; &lt;/span&gt;-v
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You shoud find the last line like "gcc version 4.6.x-google
20120106 (prerelease) (GCC)"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure the system python interpreter is python 2:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-V
&lt;span class="w"&gt;  &lt;/span&gt;Python&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.7.10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If not 2.7.x, run the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python2_dir&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pushd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python2_dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n n-Quoted"&gt;`pwd`&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;$PATH&lt;/span&gt;
&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This step is necessary on Arch Linux and a very few distributions
only. On most systems the default is already python 2.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify script/gcc-wrapper.py and add the following lines:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure the kernel&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm-eabi-&lt;span class="w"&gt; &lt;/span&gt;msm7627a-roamer2_ath_mipi_defconfig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the kernel image&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm-eabi-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now the kernel image is in arch/arm/boot/zImage. The next step is to
integrate existing initramfs with the new zImage.&lt;/p&gt;
&lt;p&gt;Android/Firefox OS boot.img is a special format. For normal Linux
distributions, zImage (vmlinuz) and initrd.gz are the two files
necessary for successful booting. On Android/Firefox OS, the two files
are integrated into a single file. That is boot.img. The handy tool
abootimg can extract and create this file. Here's the steps to generate
the new boot.img:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the original boot.img. This file can be found in factory images.
    I use "OPEN(American Standard) SD card upgrading instruction &amp;amp;
    software package(ebay)-237620B0669OPEN_US_DEV_FFOS_V1.1.0B02(for
    V1.1)" from
    &lt;a href="http://www.ztedevice.com/support/smart_phone/b5a2981a-1714-4ac7-89e1-630e93e220f8.html"&gt;http://www.ztedevice.com/support/smart_phone/b5a2981a-1714-4ac7-89e1-630e93e220f8.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extract boot.img:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;unzip&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2014012010173938&lt;/span&gt;.zip
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OPEN&lt;span class="se"&gt;\(&lt;/span&gt;American&lt;span class="se"&gt;\ &lt;/span&gt;Standard&lt;span class="se"&gt;\)\ &lt;/span&gt;SD&lt;span class="se"&gt;\ &lt;/span&gt;card&lt;span class="se"&gt;\ &lt;/span&gt;upgrading&lt;span class="se"&gt;\ &lt;/span&gt;instruction&lt;span class="se"&gt;\ \&amp;amp;\ &lt;/span&gt;software&lt;span class="se"&gt;\ &lt;/span&gt;package&lt;span class="se"&gt;\(&lt;/span&gt;ebay&lt;span class="se"&gt;\)&lt;/span&gt;-237620B0669OPEN_US_DEV_FFOS_V1.1.0B02&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;V1.1&lt;span class="se"&gt;\)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;unzip&lt;span class="w"&gt; &lt;/span&gt;US_DEV_FFOS_V1.1.0B06_UNFUS_SD_For_V1.1.zip&lt;span class="w"&gt; &lt;/span&gt;boot.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extract initrd from boot.img:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;abootimg&lt;span class="w"&gt; &lt;/span&gt;-x&lt;span class="w"&gt; &lt;/span&gt;boot.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the new boot.img:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;abootimg&lt;span class="w"&gt; &lt;/span&gt;--create&lt;span class="w"&gt; &lt;/span&gt;boot_new.img&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;bootimg.cfg&lt;span class="w"&gt; &lt;/span&gt;-k&lt;span class="w"&gt; &lt;/span&gt;../kernel/arch/arm/boot/zImage&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;initrd.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, burn the new boot.img into the device. You need adb and
fastboot installed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Enter download mode&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reboot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bootloader&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Burn the new boot.img&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;fastboot&lt;span class="w"&gt; &lt;/span&gt;flash&lt;span class="w"&gt; &lt;/span&gt;boot&lt;span class="w"&gt; &lt;/span&gt;boot_new.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fastboot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you can check the new kernel is written:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;adb&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/proc/version
Linux&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.0.21&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;yen@PC951&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;gcc&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.6.x-google&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20120106&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;prerelease&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;GCC&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#1 SMP PREEMPT Wed Aug 12 03:29:06 CST 2015&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Uncategorized"/></entry><entry><title>Mixed encoding content converter</title><link href="https://chyen.cc/blog/posts/2015/07/12/mixed-encoding-content-converter.html" rel="alternate"/><published>2015-07-12T23:03:00+08:00</published><updated>2015-07-12T23:03:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-07-12:/blog/posts/2015/07/12/mixed-encoding-content-converter.html</id><summary type="html">&lt;p&gt;標題可能下的含糊不清，意義不明，且讓我娓娓道來&lt;/p&gt;
&lt;p&gt;昨天，我用&lt;a href="https://rg3.github.io/youtube-dl/"&gt;youtube-dl&lt;/a&gt;從酷我音樂上載了林俊傑的&lt;a href="http://www.kuwo.cn/yinyue/93157/"&gt;江南&lt;/a&gt;下來。載下來的檔案是&lt;a href="https://zh.wikipedia.org/wiki/Monkey%27s_Audio"&gt;ape格式 …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;標題可能下的含糊不清，意義不明，且讓我娓娓道來&lt;/p&gt;
&lt;p&gt;昨天，我用&lt;a href="https://rg3.github.io/youtube-dl/"&gt;youtube-dl&lt;/a&gt;從酷我音樂上載了林俊傑的&lt;a href="http://www.kuwo.cn/yinyue/93157/"&gt;江南&lt;/a&gt;下來。載下來的檔案是&lt;a href="https://zh.wikipedia.org/wiki/Monkey%27s_Audio"&gt;ape格式&lt;/a&gt;，我還是第一次看到，用VLC開一堆error，所以我就用mpv聽：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mpv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;江南&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;93157.&lt;/span&gt;&lt;span class="n"&gt;ape&lt;/span&gt;
&lt;span class="nl"&gt;Playing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;江南&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;93157.&lt;/span&gt;&lt;span class="n"&gt;ape&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Audio&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;--aid=1 (ape)&lt;/span&gt;
&lt;span class="k"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ÁÖ&lt;/span&gt;&lt;span class="err"&gt;¿¡&lt;/span&gt;&lt;span class="n"&gt;½Ü&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Album&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;½&lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="n"&gt;ÄÏ&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;½&lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="n"&gt;ÄÏ&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nl"&gt;AO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="n"&gt;Hz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stereo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s16&lt;/span&gt;
&lt;span class="nl"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;一切都很好，除了metadata那邊一堆亂碼。（後來發現他們是GB2312）ffprobe出來的結果也差不多：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ffprobe&lt;span class="w"&gt; &lt;/span&gt;~/Music/songs/江南-93157.ape
ffprobe&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.7.1&lt;span class="w"&gt; &lt;/span&gt;Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2007&lt;/span&gt;-2015&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;FFmpeg&lt;span class="w"&gt; &lt;/span&gt;developers
&lt;span class="w"&gt;  &lt;/span&gt;built&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;gcc&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.1.0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;GCC&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;configuration:&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr&lt;span class="w"&gt; &lt;/span&gt;--disable-debug&lt;span class="w"&gt; &lt;/span&gt;--disable-static&lt;span class="w"&gt; &lt;/span&gt;--disable-stripping&lt;span class="w"&gt; &lt;/span&gt;--enable-avisynth&lt;span class="w"&gt; &lt;/span&gt;--enable-avresample&lt;span class="w"&gt; &lt;/span&gt;--enable-fontconfig&lt;span class="w"&gt; &lt;/span&gt;--enable-gnutls&lt;span class="w"&gt; &lt;/span&gt;--enable-gpl&lt;span class="w"&gt; &lt;/span&gt;--enable-libass&lt;span class="w"&gt; &lt;/span&gt;--enable-libbluray&lt;span class="w"&gt; &lt;/span&gt;--enable-libfreetype&lt;span class="w"&gt; &lt;/span&gt;--enable-libfribidi&lt;span class="w"&gt; &lt;/span&gt;--enable-libgsm&lt;span class="w"&gt; &lt;/span&gt;--enable-libmodplug&lt;span class="w"&gt; &lt;/span&gt;--enable-libmp3lame&lt;span class="w"&gt; &lt;/span&gt;--enable-libopencore_amrnb&lt;span class="w"&gt; &lt;/span&gt;--enable-libopencore_amrwb&lt;span class="w"&gt; &lt;/span&gt;--enable-libopenjpeg&lt;span class="w"&gt; &lt;/span&gt;--enable-libopus&lt;span class="w"&gt; &lt;/span&gt;--enable-libpulse&lt;span class="w"&gt; &lt;/span&gt;--enable-libschroedinger&lt;span class="w"&gt; &lt;/span&gt;--enable-libspeex&lt;span class="w"&gt; &lt;/span&gt;--enable-libssh&lt;span class="w"&gt; &lt;/span&gt;--enable-libtheora&lt;span class="w"&gt; &lt;/span&gt;--enable-libv4l2&lt;span class="w"&gt; &lt;/span&gt;--enable-libvorbis&lt;span class="w"&gt; &lt;/span&gt;--enable-libvpx&lt;span class="w"&gt; &lt;/span&gt;--enable-libx264&lt;span class="w"&gt; &lt;/span&gt;--enable-libx265&lt;span class="w"&gt; &lt;/span&gt;--enable-libxvid&lt;span class="w"&gt; &lt;/span&gt;--enable-shared&lt;span class="w"&gt; &lt;/span&gt;--enable-version3&lt;span class="w"&gt; &lt;/span&gt;--enable-x11grab
&lt;span class="w"&gt;  &lt;/span&gt;libavutil&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavcodec&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;41&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;41&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavformat&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;36&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;36&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavdevice&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavfilter&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;.101&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;.101
&lt;span class="w"&gt;  &lt;/span&gt;libavresample&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;libswscale&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.101&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.101
&lt;span class="w"&gt;  &lt;/span&gt;libswresample&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libpostproc&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.100
Input&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0, ape, from &amp;#39;江南-93157.ape&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Metadata:
&lt;span class="w"&gt;    &lt;/span&gt;title&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;½­ÄÏ
&lt;span class="w"&gt;    &lt;/span&gt;artist&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;ÁÖ¿¡½Ü
&lt;span class="w"&gt;    &lt;/span&gt;album&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;½­ÄÏ
&lt;span class="w"&gt;    &lt;/span&gt;encoded_by&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Exact&lt;span class="w"&gt; &lt;/span&gt;Audio&lt;span class="w"&gt; &lt;/span&gt;Copy&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Secure&lt;span class="w"&gt; &lt;/span&gt;mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;track&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;genre&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;R&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;B
&lt;span class="w"&gt;    &lt;/span&gt;date&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2004&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Duration:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;:04:27.95,&lt;span class="w"&gt; &lt;/span&gt;start:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000000,&lt;span class="w"&gt; &lt;/span&gt;bitrate:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;907&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kb/s
&lt;span class="w"&gt;    &lt;/span&gt;Stream&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0:0: Audio: ape (APE  / 0x20455041), 44100 Hz, stereo, s16p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;我就猜是ffmpeg的問題。首先先確定這些metadata是哪種格式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;江南-93157.ape
江南-93157.ape:&lt;span class="w"&gt; &lt;/span&gt;Audio&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;ID3&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.3.0,&lt;span class="w"&gt; &lt;/span&gt;contains:&lt;span class="w"&gt; &lt;/span&gt;Monkey&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;s&lt;span class="w"&gt; &lt;/span&gt;Audio&lt;span class="w"&gt; &lt;/span&gt;compressed&lt;span class="w"&gt; &lt;/span&gt;format&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3970&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;fast&lt;span class="w"&gt; &lt;/span&gt;compression,&lt;span class="w"&gt; &lt;/span&gt;stereo,&lt;span class="w"&gt; &lt;/span&gt;sample&lt;span class="w"&gt; &lt;/span&gt;rate&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;44100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;嗯是ID3v2
2.3。ID3v2的header開頭是"ID3"。先在ffmpeg的code裡搜尋這個字串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;quot;ID3&amp;quot;&amp;#39;&lt;/span&gt;
libavformat/asfdec_o.c:&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;!strcmp&lt;span class="o"&gt;(&lt;/span&gt;name,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ID3&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;//&lt;span class="w"&gt; &lt;/span&gt;handle&lt;span class="w"&gt; &lt;/span&gt;ID3&lt;span class="w"&gt; &lt;/span&gt;tag
libavformat/asfdec_f.c:&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;!strcmp&lt;span class="o"&gt;(&lt;/span&gt;key,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ID3&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;//&lt;span class="w"&gt; &lt;/span&gt;handle&lt;span class="w"&gt; &lt;/span&gt;ID3&lt;span class="w"&gt; &lt;/span&gt;tag
libavformat/id3v2.h:&lt;span class="w"&gt; &lt;/span&gt;*&lt;span class="w"&gt; &lt;/span&gt;Default&lt;span class="w"&gt; &lt;/span&gt;magic&lt;span class="w"&gt; &lt;/span&gt;bytes&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ID3v2&lt;span class="w"&gt; &lt;/span&gt;header:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ID3&amp;quot;&lt;/span&gt;
libavformat/id3v2.h:#define&lt;span class="w"&gt; &lt;/span&gt;ID3v2_DEFAULT_MAGIC&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ID3&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;雖然asfdec_f.c跟asfdec_o.c聽起來不太像是decode
ape的檔，不過應該都差不多。打開asfdec_o.c來看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;            else if (!strcmp(name, &amp;quot;ID3&amp;quot;)) // handle ID3 tag
                get_id3_tag(s, val_len);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;一步一步追查下去，可以知道函數呼叫的順序是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;get_id3_tag() in libavformat/asfdec_o.c&lt;/li&gt;
&lt;li&gt;ff_id3v2_read() in libavformat/id3v2.c, 3~5也是在這個檔案裡&lt;/li&gt;
&lt;li&gt;id3v2_read_internal()&lt;/li&gt;
&lt;li&gt;id3v2_parse()&lt;/li&gt;
&lt;li&gt;read_ttag()&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;read_ttag()裡面，他先做decode_str()：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    if (decode_str(s, pb, encoding, &amp;amp;dst, &amp;amp;taglen) &amp;lt; 0) {
        av_log(s, AV_LOG_ERROR, &amp;quot;Error reading frame %s, skipped\n&amp;quot;, key);
        return;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;decode_str()用到的encoding參數是從前一個byte讀出來的。我下載下來的檔案這個地方是0，也就是&lt;a href="https://zh.wikipedia.org/wiki/ISO/IEC_8859-1"&gt;ISO-8859-1&lt;/a&gt;。不過那些字串顯然不是ISO-8859-1，顯示出來就變成亂碼。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ID3v2_ENCODING_ISO8859&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;avio_r8(pb)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;PUT_UTF8(ch,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tmp,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;avio_w8(dynbuf,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tmp)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;left--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;這裡的關鍵是PUT_UTF8，他做的事是把UTF-32轉換成UTF-8（詳見libavutil/common.h）。理論上只要把UTF-8轉回UTF-32，再用GB2312顯示出來就可以了。&lt;/p&gt;
&lt;p&gt;不過這裡有個問題。ffprobe的結果裡面"江南-93157.ape"那一行是正確的UTF-8，其他有亂碼的地方是GB2312，不能全部用同一種encoding顯示出來。我的作法是每行分開處理。&lt;/p&gt;
&lt;p&gt;剛好最近聽到&lt;a href="https://chardet.github.io/"&gt;chardet&lt;/a&gt;，一個可以自動幫你偵測encoding的python
module，我就拿來用用看。最後的code放在&lt;a href="https://chyen.cc/pub/convert_encoding.py"&gt;這兒&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;結果如下，大功告成！&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ffprobe&lt;span class="w"&gt; &lt;/span&gt;~/Music/songs/江南-93157.ape&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;convert_encoding.py
Encoding:&lt;span class="w"&gt; &lt;/span&gt;GB2312&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;confidence&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.99

ffprobe&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.7.1&lt;span class="w"&gt; &lt;/span&gt;Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2007&lt;/span&gt;-2015&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;FFmpeg&lt;span class="w"&gt; &lt;/span&gt;developers
&lt;span class="w"&gt;  &lt;/span&gt;built&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;gcc&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.1.0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;GCC&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;configuration:&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr&lt;span class="w"&gt; &lt;/span&gt;--disable-debug&lt;span class="w"&gt; &lt;/span&gt;--disable-static&lt;span class="w"&gt; &lt;/span&gt;--disable-stripping&lt;span class="w"&gt; &lt;/span&gt;--enable-avisynth&lt;span class="w"&gt; &lt;/span&gt;--enable-avresample&lt;span class="w"&gt; &lt;/span&gt;--enable-fontconfig&lt;span class="w"&gt; &lt;/span&gt;--enable-gnutls&lt;span class="w"&gt; &lt;/span&gt;--enable-gpl&lt;span class="w"&gt; &lt;/span&gt;--enable-libass&lt;span class="w"&gt; &lt;/span&gt;--enable-libbluray&lt;span class="w"&gt; &lt;/span&gt;--enable-libfreetype&lt;span class="w"&gt; &lt;/span&gt;--enable-libfribidi&lt;span class="w"&gt; &lt;/span&gt;--enable-libgsm&lt;span class="w"&gt; &lt;/span&gt;--enable-libmodplug&lt;span class="w"&gt; &lt;/span&gt;--enable-libmp3lame&lt;span class="w"&gt; &lt;/span&gt;--enable-libopencore_amrnb&lt;span class="w"&gt; &lt;/span&gt;--enable-libopencore_amrwb&lt;span class="w"&gt; &lt;/span&gt;--enable-libopenjpeg&lt;span class="w"&gt; &lt;/span&gt;--enable-libopus&lt;span class="w"&gt; &lt;/span&gt;--enable-libpulse&lt;span class="w"&gt; &lt;/span&gt;--enable-libschroedinger&lt;span class="w"&gt; &lt;/span&gt;--enable-libspeex&lt;span class="w"&gt; &lt;/span&gt;--enable-libssh&lt;span class="w"&gt; &lt;/span&gt;--enable-libtheora&lt;span class="w"&gt; &lt;/span&gt;--enable-libv4l2&lt;span class="w"&gt; &lt;/span&gt;--enable-libvorbis&lt;span class="w"&gt; &lt;/span&gt;--enable-libvpx&lt;span class="w"&gt; &lt;/span&gt;--enable-libx264&lt;span class="w"&gt; &lt;/span&gt;--enable-libx265&lt;span class="w"&gt; &lt;/span&gt;--enable-libxvid&lt;span class="w"&gt; &lt;/span&gt;--enable-shared&lt;span class="w"&gt; &lt;/span&gt;--enable-version3&lt;span class="w"&gt; &lt;/span&gt;--enable-x11grab
&lt;span class="w"&gt;  &lt;/span&gt;libavutil&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavcodec&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;41&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;41&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavformat&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;36&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;36&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavdevice&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;56&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libavfilter&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;.101&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;.101
&lt;span class="w"&gt;  &lt;/span&gt;libavresample&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;libswscale&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.101&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.101
&lt;span class="w"&gt;  &lt;/span&gt;libswresample&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.100
&lt;span class="w"&gt;  &lt;/span&gt;libpostproc&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.100&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.100
Input&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0, ape, from &amp;#39;江南-93157.ape&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Metadata:
&lt;span class="w"&gt;    &lt;/span&gt;title&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;江南
&lt;span class="w"&gt;    &lt;/span&gt;artist&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;林俊杰
&lt;span class="w"&gt;    &lt;/span&gt;album&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;江南
&lt;span class="w"&gt;    &lt;/span&gt;encoded_by&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Exact&lt;span class="w"&gt; &lt;/span&gt;Audio&lt;span class="w"&gt; &lt;/span&gt;Copy&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Secure&lt;span class="w"&gt; &lt;/span&gt;mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;track&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;genre&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;R&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;B
&lt;span class="w"&gt;    &lt;/span&gt;date&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2004&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Duration:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;:04:27.95,&lt;span class="w"&gt; &lt;/span&gt;start:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000000,&lt;span class="w"&gt; &lt;/span&gt;bitrate:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;907&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kb/s
&lt;span class="w"&gt;    &lt;/span&gt;Stream&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0:0: Audio: ape (APE  / 0x20455041), 44100 Hz, stereo, s16p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Uncategorized"/></entry><entry><title>Placement new</title><link href="https://chyen.cc/blog/posts/2015/04/21/placement-new.html" rel="alternate"/><published>2015-04-21T20:53:00+08:00</published><updated>2015-04-21T20:53:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-04-21:/blog/posts/2015/04/21/placement-new.html</id><summary type="html">&lt;p&gt;For advanced C++ programmers, placement new is nothing special. It can
initialize a pre-allocated memory block. However, placement new is more
than what you think. It accpets an arbitrary number of arguments of
arbitrary types.&lt;/p&gt;
&lt;p&gt;See the following code snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;

&lt;span class="kr"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;operator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;For advanced C++ programmers, placement new is nothing special. It can
initialize a pre-allocated memory block. However, placement new is more
than what you think. It accpets an arbitrary number of arguments of
arbitrary types.&lt;/p&gt;
&lt;p&gt;See the following code snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;

&lt;span class="kr"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;operator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;noexcept&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;l&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That means, you can put any possible C++ type as the second argument of
placement new operators.&lt;/p&gt;
&lt;p&gt;Is it used in real cases? In fact, nothrow new operator in standard C++
utilizes this feature. See the implementation in
&lt;a href="https://github.com/llvm-mirror/libcxx/blob/master/src/new.cpp#L71-88"&gt;libc++&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another usage can be found in
&lt;a href="https://github.com/ivmai/bdwgc/blob/master/include/gc_cpp.h#L391-408"&gt;libgc&lt;/a&gt;,
a C/C++ garbage collection library.&lt;/p&gt;</content><category term="Uncategorized"/></entry><entry><title>Bypass kuwo.cn IP filtering 跳過酷我音樂的IP檢測</title><link href="https://chyen.cc/blog/posts/2015/02/25/bypass-kuwo-cn-ip-filtering-%E8%B7%B3%E9%81%8E%E9%85%B7%E6%88%91%E9%9F%B3%E6%A8%82%E7%9A%84ip%E6%AA%A2%E6%B8%AC.html" rel="alternate"/><published>2015-02-25T21:41:00+08:00</published><updated>2015-02-25T21:41:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2015-02-25:/blog/posts/2015/02/25/bypass-kuwo-cn-ip-filtering-跳過酷我音樂的ip檢測.html</id><summary type="html">&lt;p&gt;&lt;a href="http://www.kuwo.cn"&gt;kuwo.cn&lt;/a&gt; is a popular music sharing platform in
China. Chinese users can enjoy latest and high-quality music on it.
However, it exploits some trick to prevent users outside Mainland China
from listening music flawlessly. For example, when you visit
&lt;a href="http://www.kuwo.cn/yinyue/112882/"&gt;http://www.kuwo.cn/yinyue/112882/&lt;/a&gt; from a country different …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://www.kuwo.cn"&gt;kuwo.cn&lt;/a&gt; is a popular music sharing platform in
China. Chinese users can enjoy latest and high-quality music on it.
However, it exploits some trick to prevent users outside Mainland China
from listening music flawlessly. For example, when you visit
&lt;a href="http://www.kuwo.cn/yinyue/112882/"&gt;http://www.kuwo.cn/yinyue/112882/&lt;/a&gt; from a country different from China
(Taiwan in my case), you will be redirected to the homepage immediately
before you can click the play button and enjoy the relaxing voice. Out
of curiousity and my own need, I study a small portion of it source code
and find it simple to accomplish such IP filtering, and I also find out
a lightweight approach to bypass such restriction.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.kuwo.cn"&gt;酷我音樂&lt;/a&gt;是一個中國著名的音樂分享平台。中國用戶可以在上面享受最新的高音質音樂。然而，這個網站使用了一些手段來防止中國大陸以外的用戶順利的聽音樂。舉例來說，如果你從一個中國以外的國家連上&lt;a href="http://www.kuwo.cn/yinyue/112882/"&gt;http://www.kuwo.cn/yinyue/112882/&lt;/a&gt;（就我的例子是台灣），你就會在按下播放鈕之前被重新導向到首頁，而無法享受這令人放鬆的嗓音。出於好奇心和我自己的需求，我對他的一小部份原始碼進行研究，發現他是用很簡單的方法來達成這種IP檢測。而我也找出一種輕巧的手段來跳過這種限制。&lt;/p&gt;
&lt;p&gt;First, observing the source code of kuwo pages. By default it's not
possible to view the source code in the browser due to redirections, so
I download the page with curl:&lt;/p&gt;
&lt;p&gt;首先，觀察這個網頁的原始碼。由於重新導向的緣故，預設情況下是無法用瀏覽器看到網頁原始碼，於是我用curl下載這個網頁：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://www.kuwo.cn/yinyue/112882/
//...previous&lt;span class="w"&gt; &lt;/span&gt;irrelevant&lt;span class="w"&gt; &lt;/span&gt;codes&lt;span class="w"&gt; &lt;/span&gt;omitted&lt;span class="w"&gt; &lt;/span&gt;省略前面無關的code...
&lt;span class="w"&gt;    &lt;/span&gt;/*验证ip是否是国外四国的*/
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;checkIp&lt;span class="o"&gt;(){&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;jQuery.ajax&lt;span class="o"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;url&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/yy/PlayCheckIp&amp;#39;&lt;/span&gt;,
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;get&amp;#39;&lt;/span&gt;,
&lt;span class="w"&gt;             &lt;/span&gt;data&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;             &lt;/span&gt;success&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;data&lt;span class="o"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{\&amp;quot;stauts\&amp;quot;:400}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;window.location&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://www.kuwo.cn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;checkIp&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
//...some&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;codes&lt;span class="w"&gt; &lt;/span&gt;一些其他code...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Obviously, the site checks your IP and redirect you to their homepage if
you're not welcomed. You can visit &lt;a href="http://www.kuwo.cn/yy/PlayCheckIp"&gt;http://www.kuwo.cn/yy/PlayCheckIp&lt;/a&gt;
and see what's in it. The simplest way to escape the awful situation is
blocking all traffic to the check point. If the checking request fails,
nothing happens. I have done it by adding a custom rule to
&lt;a href="https://adblockplus.org/"&gt;AdblockPlus&lt;/a&gt;. Now I can dive in the ocean of
music without annoying redirections!&lt;/p&gt;
&lt;p&gt;顯然，這個網站會檢查你的IP，而且在你不受歡迎時把你重新導向到首頁。你可以連上&lt;a href="http://www.kuwo.cn/yy/PlayCheckIp"&gt;http://www.kuwo.cn/yy/PlayCheckIp&lt;/a&gt;看看裡面有什麼。跳脫這種糟糕的情況最簡單的方法是阻擋所有通往檢查點的流量。如果檢查的請求沒有成功，什麼事都不會發生。我在&lt;a href="https://adblockplus.org/"&gt;AdblockPlus&lt;/a&gt;裡加了一條自訂的過濾條件，然後就成功了。現在我可以徜徉音樂之海而不會遇到惱人的重新導向。&lt;/p&gt;
&lt;p&gt;Other sites have some other tricks to prevent non-China users from
watching their videos or listening to their songs. Such examples include
&lt;a href="http://www.letv.com/"&gt;letv.com&lt;/a&gt; and &lt;a href="http://www.youku.com/"&gt;youku.com&lt;/a&gt;.
Here are some examples:&lt;/p&gt;
&lt;p&gt;其他網站有一些其他技巧來阻止非中國的使用者看他們的影片或聽他們的音樂。這樣的例子有&lt;a href="http://www.letv.com/"&gt;樂視&lt;/a&gt;和&lt;a href="http://www.youku.com/"&gt;優酷&lt;/a&gt;。這兒有些例子：&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.letv.com/ptv/vplay/21511001.html"&gt;http://www.letv.com/ptv/vplay/21511001.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There's a project &lt;a href="https://github.com/zhuzhuor/Unblock-Youku"&gt;Unblock
Youku&lt;/a&gt; aiming on such issues.
However, kuwo.cn is not supported as of now. In fact, there's not need
to proxy the traffic of kuwo IP detection. Just block and ignore it.&lt;/p&gt;
&lt;p&gt;有個專案&lt;a href="https://github.com/zhuzhuor/Unblock-Youku"&gt;Unblock
Youku&lt;/a&gt;力圖解決這些問題，然而到目前為止酷我還沒被支援。實際上也沒有必要轉送酷我IP檢測的流量。只要阻擋然後忽略就行了。&lt;/p&gt;
&lt;p&gt;Among these sites, what kuwo.cn uses is the simplest, but maybe it's
why there's no discussion about it. I googled PlayCheckIp and got
nothing meaningful. I hope this short post can help people outside
Mainland China have the same rights to enjoy great music :)&lt;/p&gt;
&lt;p&gt;在這些網站裡，酷我用的方法是最簡單的，但這也可能是為何都沒有相關的討論。我在google上搜尋PlayCheckIp沒有得到有用的資訊。我希望這篇文章可以幫助中國大陸以外的使用者也能擁有享受美妙音樂的權利
:)&lt;/p&gt;</content><category term="Security"/></entry><entry><title>Tonalities</title><link href="https://chyen.cc/blog/posts/2014/09/17/tonalities.html" rel="alternate"/><published>2014-09-17T18:56:00+08:00</published><updated>2014-09-17T18:56:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2014-09-17:/blog/posts/2014/09/17/tonalities.html</id><summary type="html">&lt;p&gt;Use my ear with the help of &lt;a href="http://vmpk.sourceforge.net/"&gt;vmpk&lt;/a&gt;, it's
not difficult to determine the tonality of popular songs. Here are
results:
&lt;a href="http://chyen.twbbs.org/yen/pub/major.html"&gt;http://chyen.twbbs.org/yen/pub/major.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is a Matlab toolbox developed by University of Jyväskylä named
&lt;a href="https://www.jyu.fi/hum/laitokset/musiikki/en/research/coe/materials/mirtoolbox"&gt;MIRtoolbox&lt;/a&gt;,
aiming to analyzing music clips with cutting-edge algorithms. It …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Use my ear with the help of &lt;a href="http://vmpk.sourceforge.net/"&gt;vmpk&lt;/a&gt;, it's
not difficult to determine the tonality of popular songs. Here are
results:
&lt;a href="http://chyen.twbbs.org/yen/pub/major.html"&gt;http://chyen.twbbs.org/yen/pub/major.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is a Matlab toolbox developed by University of Jyväskylä named
&lt;a href="https://www.jyu.fi/hum/laitokset/musiikki/en/research/coe/materials/mirtoolbox"&gt;MIRtoolbox&lt;/a&gt;,
aiming to analyzing music clips with cutting-edge algorithms. It
provides a scientific way to determine the tonality. However, I still
like the primitive way :)&lt;/p&gt;</content><category term="Uncategorized"/></entry><entry><title>Quadcopters from TED.com</title><link href="https://chyen.cc/blog/posts/2013/09/12/quadcopters-from-ted-com.html" rel="alternate"/><published>2013-09-12T21:56:00+08:00</published><updated>2013-09-12T21:56:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-09-12:/blog/posts/2013/09/12/quadcopters-from-ted-com.html</id><content type="html">&lt;p&gt;&lt;a href="http://www.ted.com/talks/raffaello_d_andrea_the_astounding_athletic_power_of_quadcopters.html"&gt;http://www.ted.com/talks/raffaello_d_andrea_the_astounding_athletic_power_of_quadcopters.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A video clip played on the first class of Control Systems.&lt;/p&gt;</content><category term="Something fun"/></entry><entry><title>孤單北半球</title><link href="https://chyen.cc/blog/posts/2013/07/30/%E5%AD%A4%E5%96%AE%E5%8C%97%E5%8D%8A%E7%90%83.html" rel="alternate"/><published>2013-07-30T19:12:00+08:00</published><updated>2013-07-30T19:12:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-07-30:/blog/posts/2013/07/30/孤單北半球.html</id><summary type="html">&lt;style type="text/css"&gt;
/* for vimdiff below */
table { color: #d0d0d0; background-color: #3a3a3a; }
.DiffChange { background-color: #3a3a3a; padding: 0px; margin: 0px; }
.DiffText { background-color: #5f005f; }
table { table-layout: fixed; }
table, tbody { width: 100%; margin: 0; padding: 0; }
td { width: 50.0%; white-space: nowrap; }
&lt;/style&gt;
&lt;p&gt;有一天在和幾個同學唸書，有人忽然 …&lt;/p&gt;</summary><content type="html">&lt;style type="text/css"&gt;
/* for vimdiff below */
table { color: #d0d0d0; background-color: #3a3a3a; }
.DiffChange { background-color: #3a3a3a; padding: 0px; margin: 0px; }
.DiffText { background-color: #5f005f; }
table { table-layout: fixed; }
table, tbody { width: 100%; margin: 0; padding: 0; }
td { width: 50.0%; white-space: nowrap; }
&lt;/style&gt;
&lt;p&gt;有一天在和幾個同學唸書，有人忽然提到孤單北半球這首歌。這首歌最早是誰唱的？眾說紛紜，不一而足。或曰林依晨，或曰歐得洋。上Google一查，原唱是歐得洋。長久以來，我和幾位同學一直以為是林依晨原唱，竟是個天大的笑話！先入為主的成見和憑空臆測使我們長久以來抱持錯誤的認知，若非網路上訊息的快速流通，孤單北半球這首歌的原唱，只怕是和我們的心靈無緣了。胡適嘗曰：『大膽假設，小心求證』，但在人來人往，駢肩雜沓的年代，連求證也難了，實不可不慎！&lt;/p&gt;
&lt;p&gt;歐得洋和林依晨的版本，兩者又是另一個故事。就音色言，各有別致韻味，此乃見仁見智，暫且不表。且說兩者歌詞。以下歐得洋版的歌詞（取自&lt;a href="http://mojim.com/"&gt;魔鏡歌詞網&lt;/a&gt;）：&lt;/p&gt;
&lt;pre&gt;
&lt;div style="color: #446622"&gt;
用我的晚安陪你　吃早餐　記得把想念　存進撲滿
我　望著滿天星　在閃　聽牛郎對織女說　要勇敢

別怕我們在地球　的兩端　看我的問候　騎著魔毯
飛　用光速飛到你面前　要你能看到十字星有北極星作伴

少了我的手臂當枕頭　你習不習慣
你的望遠鏡望不到　我北半球的孤單
太平洋的潮水跟著地球　來迴旋轉
我會耐心地等　隨時歡迎你靠岸

少了我的懷抱當暖爐　你習不習慣
E給你照片看不到　我北半球的孤單
世界再大兩顆真心就能　互相取暖
想念不會偷懶　我的夢通通給你保管
&lt;/div&gt;
&lt;/pre&gt;

&lt;p&gt;而林依晨版的歌詞則是：&lt;/p&gt;
&lt;pre&gt;
&lt;div style="color: #446622"&gt;
用你的早安陪我　吃晚餐　記得把想念　存進撲滿
我　望著滿天星　在閃　聽牛郎對織女說　要勇敢

不怕我們在地球　的兩端　看你的問候　騎著魔毯
飛　用光速飛到我面前　你讓我看到北極星有十字星作伴

少了你的手臂當枕頭　我還不習慣
你的望遠鏡望不到　我北半球的孤單
太平洋的潮水跟著地球　來迴旋轉
我會耐心地等　等你有一天靠岸

少了你的懷抱當暖爐　我還不習慣
E給你照片看不到　我北半球的孤單
世界再大兩顆真心就能　互相取暖
想念不會偷懶　我的夢通通給你保管
&lt;/div&gt;
&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/7475244/save-vimdiff-output"&gt;vimdiff&lt;/a&gt;一下：&lt;/p&gt;
&lt;table width="100%" border="1"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;孤單北半球-林依晨.txt&lt;/td&gt;
&lt;td&gt;孤單北半球-歐得洋.txt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td valign="top"&gt;
&lt;pre class="DiffChange"&gt;
&lt;div&gt;用&lt;span class="DiffText"&gt;你的早安陪我　吃晚&lt;/span&gt;餐　記得把想念　存進撲滿 
我　望著滿天星　在閃　聽牛郎對織女說　要勇敢

&lt;span class="DiffText"&gt;不怕我們在地球　的兩端　看你&lt;/span&gt;的問候　騎著魔毯
飛　用光速飛到&lt;span class="DiffText"&gt;我面前　你讓我看到北極星有十字&lt;/span&gt;星作伴

少了&lt;span class="DiffText"&gt;你的手臂當枕頭　我還&lt;/span&gt;不習慣
你的望遠鏡望不到　我北半球的孤單
太平洋的潮水跟著地球　來迴旋轉
我會耐心地等　&lt;span class="DiffText"&gt;等你有一天&lt;/span&gt;靠岸

少了&lt;span class="DiffText"&gt;你的懷抱當暖爐　我還&lt;/span&gt;不習慣
E給你照片看不到　我北半球的孤單
世界再大兩顆真心就能　互相取暖
想念不會偷懶　我的夢通通給你保管

&lt;/div&gt;
&lt;/pre&gt;
&lt;/td&gt;
&lt;td valign="top"&gt;
&lt;pre class="DiffChange"&gt;
&lt;div&gt;用&lt;span class="DiffText"&gt;我的晚安陪你　吃早&lt;/span&gt;餐　記得把想念　存進撲滿
我　望著滿天星　在閃　聽牛郎對織女說　要勇敢

&lt;span class="DiffText"&gt;別怕我們在地球　的兩端　看我&lt;/span&gt;的問候　騎著魔毯
飛　用光速飛到&lt;span class="DiffText"&gt;你面前　要你能看到十字星有北極&lt;/span&gt;星作伴

少了&lt;span class="DiffText"&gt;我的手臂當枕頭　你習&lt;/span&gt;不習慣
你的望遠鏡望不到　我北半球的孤單
太平洋的潮水跟著地球　來迴旋轉
我會耐心地等　&lt;span class="DiffText"&gt;隨時歡迎你&lt;/span&gt;靠岸

少了&lt;span class="DiffText"&gt;我的懷抱當暖爐　你習&lt;/span&gt;不習慣
E給你照片看不到　我北半球的孤單
世界再大兩顆真心就能　互相取暖
想念不會偷懶　我的夢通通給你保管

&lt;/div&gt;
&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;大多相同，而不同之處畫龍點睛，有如兩人對話。編詞的用心，可見一斑。&lt;/p&gt;</content><category term="Something fun"/></entry><entry><title>中和路 and 永和路</title><link href="https://chyen.cc/blog/posts/2013/07/22/zhonghe-yonghe.html" rel="alternate"/><published>2013-07-22T13:57:00+08:00</published><updated>2013-07-22T13:57:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-07-22:/blog/posts/2013/07/22/zhonghe-yonghe.html</id><summary type="html">&lt;!-- start time? --&gt;
&lt;iframe width="493" height="370" src="https://www.youtube.com/embed/L7T2nQD_mt8"
 frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;「中永和之歌」
&lt;a href="http://msuvictor.pixnet.net/blog/post/31737885-%5B%E5%A6%99%E8%A8%80%5D-%E3%80%8C%E7%99%BE%E6%85%95%E9%81%94%E4%B8%AD%E6%B0%B8%E5%92%8C%E3%80%8D-%E8%B7%AF%E5%90%8D%E6%9C%89%E5%A4%A0%E4%BA%82"&gt;http://msuvictor.pixnet.net/blog/post/31737885-%5B%E5%A6%99%E8%A8%80%5D-%E3%80%8C%E7%99%BE%E6%85%95%E9%81%94%E4%B8%AD%E6%B0%B8%E5%92%8C%E3%80%8D-%E8%B7%AF%E5%90%8D …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;!-- start time? --&gt;
&lt;iframe width="493" height="370" src="https://www.youtube.com/embed/L7T2nQD_mt8"
 frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;「中永和之歌」
&lt;a href="http://msuvictor.pixnet.net/blog/post/31737885-%5B%E5%A6%99%E8%A8%80%5D-%E3%80%8C%E7%99%BE%E6%85%95%E9%81%94%E4%B8%AD%E6%B0%B8%E5%92%8C%E3%80%8D-%E8%B7%AF%E5%90%8D%E6%9C%89%E5%A4%A0%E4%BA%82"&gt;http://msuvictor.pixnet.net/blog/post/31737885-%5B%E5%A6%99%E8%A8%80%5D-%E3%80%8C%E7%99%BE%E6%85%95%E9%81%94%E4%B8%AD%E6%B0%B8%E5%92%8C%E3%80%8D-%E8%B7%AF%E5%90%8D%E6%9C%89%E5%A4%A0%E4%BA%82&lt;/a&gt;&lt;/p&gt;
&lt;!-- looks dirty :/ --&gt;</content><category term="misc"/><category term="from-wordpress"/></entry><entry><title>OpenCV: strange return value of cvWaitKey()</title><link href="https://chyen.cc/blog/posts/2013/04/20/opencv-cvWaitKey.html" rel="alternate"/><published>2013-04-20T02:59:00+08:00</published><updated>2013-04-20T02:59:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-04-20:/blog/posts/2013/04/20/opencv-cvWaitKey.html</id><summary type="html">&lt;p&gt;In OpenCV, cvWaitKey() might be one of most frequently used functions in interactive applications. For example (code in C++):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cvWaitKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 27 is the key code of Escape&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This works in Windows, but not work in Ubuntu. Both C++ version and python version can't catch …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In OpenCV, cvWaitKey() might be one of most frequently used functions in interactive applications. For example (code in C++):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cvWaitKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 27 is the key code of Escape&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This works in Windows, but not work in Ubuntu. Both C++ version and python version can't catch escape key in Ubuntu. So annoying!&lt;/p&gt;
&lt;p&gt;To find out what happened, I modify the code to print the return value of cvWaitKey(). The result for escape key is 1048603. Those who often coding might has a sense on this number - it's close to 1048576! In fact, it's 1048576+27. So strange! I google by using keyword "cvWaitKey 1048576", and found &lt;a href="http://tech.dir.groups.yahoo.com/group/OpenCV/message/46857"&gt;A Yahoo group question&lt;/a&gt; discussing this. Then I go to &lt;a href="http://sourceforge.net/projects/opencvlibrary/"&gt;OpenCV on SourceForge&lt;/a&gt; and download the archived code. My downloaded version is 2.4.5. In &lt;code&gt;modules/highgui/src/window_gtk.cpp&lt;/code&gt;, the body of &lt;code&gt;cvWaitKey()&lt;/code&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;CV_IMPL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cvWaitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef HAVE_GTHREAD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_started&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g_thread_self&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;window_thread&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;gboolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_last_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// wait for signal or timeout if delay &amp;gt; 0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;GTimeVal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;g_get_current_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;g_time_val_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;g_cond_timed_wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond_have_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_key_mutex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;g_cond_wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond_have_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_key_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;my_last_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;g_mutex_unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_key_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hg_windows&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_last_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;guint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g_timeout_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;icvAlarm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gtk_main_iteration_do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hg_windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;g_source_remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef HAVE_GTHREAD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Seems the returned value is not modified in this function. After simple search, I found it's set in &lt;code&gt;icvOnKeyPress()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gboolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;icvOnKeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GtkWidget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/*widget*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;GdkEventKey&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gpointer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/*user_data*/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keyval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;GDK_Escape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;GDK_Return&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;GDK_Linefeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;GDK_Tab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;keyval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef HAVE_GTHREAD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_started&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g_mutex_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_key_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef HAVE_GTHREAD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_started&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// signal any waiting threads&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;g_cond_broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond_have_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;g_mutex_unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_key_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The consequence is clear: the returned value of &lt;code&gt;cvWaitKey()&lt;/code&gt; is not exactly the key code. Practically, I use the code snippet below to detect keys:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cvWaitKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xfffff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// only pick 20 bits from LSB&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is what OpenCV in GTK+ happened. How about other platforms? I examined &lt;code&gt;window_QT.cpp&lt;/code&gt;, &lt;code&gt;window_w32.cpp&lt;/code&gt; (it's for windows), &lt;code&gt;window_carbon.cpp&lt;/code&gt;, &lt;code&gt;window_cocoa.mm&lt;/code&gt; (above two are for MacOSX), and all of them give exactly the key code from from underlying library in &lt;code&gt;cvWaitKey()&lt;/code&gt;. I searched on &lt;a href="https://code.ros.org/trac/opencv/browser/trunk/"&gt;the svn repository&lt;/a&gt;, and found that &lt;a href="https://code.ros.org/trac/opencv/browser/trunk/opencv/src/highgui?rev=617"&gt;revision 617&lt;/a&gt; is the first time &lt;code&gt;window_gtk.cpp&lt;/code&gt; emerged. Then I searched in branch &lt;code&gt;MACOSX_DEVELOPMENT&lt;/code&gt;. The first time &lt;code&gt;window_gtk.cpp&lt;/code&gt; appeared is &lt;a href="https://code.ros.org/trac/opencv/browser/branches/MACOSX_DEVELOPMENT/opencv/src/highgui?rev=508"&gt;revision 508&lt;/a&gt;. Here the strange line exists, and from the commit log, I can find nothing.&lt;/p&gt;
&lt;p&gt;Again, it's an example that cross-platform libraries behave differently on different platforms. But this time the condition is slightly different: the difference is caused by totally different codes, but not the programmer's insufficient sense on specific platforms.&lt;/p&gt;</content><category term="misc"/><category term="from-wordpress"/></entry><entry><title>SFTP initial path</title><link href="https://chyen.cc/blog/posts/2013/04/20/sftp-initial-path.html" rel="alternate"/><published>2013-04-20T02:33:00+08:00</published><updated>2013-04-20T02:33:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-04-20:/blog/posts/2013/04/20/sftp-initial-path.html</id><summary type="html">&lt;p&gt;Google "sftp default path"，大多數是用chroot的方式。但我的需求是一開始先切換到某個home以外的目錄，而不是將user鎖在某個目錄底下。轉念一想，sftp-server也只是一個用user的身份執行的process，其實只要從.bashrc或.profile之類的檔案下手即可。&lt;/p&gt;
&lt;p&gt;首先看看Ubuntu的default .bashrc的前幾行(在 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Google "sftp default path"，大多數是用chroot的方式。但我的需求是一開始先切換到某個home以外的目錄，而不是將user鎖在某個目錄底下。轉念一想，sftp-server也只是一個用user的身份執行的process，其實只要從.bashrc或.profile之類的檔案下手即可。&lt;/p&gt;
&lt;p&gt;首先看看Ubuntu的default .bashrc的前幾行(在/etc/skel/.bashrc)：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/.bashrc: executed by bash(1) for non-login shells.&lt;/span&gt;
&lt;span class="c1"&gt;# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)&lt;/span&gt;
&lt;span class="c1"&gt;# for examples&lt;/span&gt;

&lt;span class="c1"&gt;# If not running interactively, don&amp;#39;t do anything&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-z&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PS1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;PS1類似windows下的prompt，當用ssh tunnel或sftp登入時，這個environment variable不存在。如果想要在這幾種登入方法下執行某些指令，必須加在這幾行的前面。我先用"env &amp;gt; ~/tmp_env" 觀察用不同方式登入時environment variables有何不同。結果是用SSH登入時有數個&lt;code&gt;SSH_&lt;/code&gt;開始的environment variables。如此即可達成需求（加在&lt;code&gt;.bashrc&lt;/code&gt;的最前面）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# if not in an interactive shell, and login by ssh, then it&amp;#39;s sftp&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-z&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PS1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SSH_CLIENT&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/export&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;其中&lt;code&gt;cd ~/export&lt;/code&gt;是想要的initial path，也可以執行其他command，不過要注意的是不能有輸出（echo之類），否則ssh可能會 &lt;a href="http://www.linuxquestions.org/questions/linux-networking-3/ssh-login-works-but-sftp-doesnt-369380/"&gt;無法用sftp登入&lt;/a&gt;。&lt;/p&gt;</content><category term="misc"/><category term="from-wordpress"/></entry><entry><title>Hello world!</title><link href="https://chyen.cc/blog/posts/2013/04/07/hello-world.html" rel="alternate"/><published>2013-04-07T04:13:00+08:00</published><updated>2013-04-07T04:13:00+08:00</updated><author><name>Chih-Hsuan Yen</name></author><id>tag:chyen.cc,2013-04-07:/blog/posts/2013/04/07/hello-world.html</id><summary type="html">&lt;p&gt;Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!&lt;/p&gt;
&lt;p&gt;It's the second time I install WordPress on my Apache server. The first time is not long ago, but everything seems easier then ever! WordPress is primarily written in PHP, so a little knowledge for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!&lt;/p&gt;
&lt;p&gt;It's the second time I install WordPress on my Apache server. The first time is not long ago, but everything seems easier then ever! WordPress is primarily written in PHP, so a little knowledge for PHP is inevitable, but WordPress makes it understandable for non-PHPer. Just some backgrounds of managing server make everything goes well. For those passionate in sharing ideas but not familiar with tips of manipulating computers, online providers are everywhere. There might be bugs and security issues in such a sophisticated system, but the its popularity and the essence of open source makes bug fixing fast. Another exciting feature of WordPress is that I can worry nothing about permission issues. I just extract the archive to my home directory, thus I'm the only owner of all WordPress files. During the installation, I didn't change the permission or the owner of any files or directories. The only step I've done other than editing wp-config.php is create a new user an the corresponding database on the MySQL server. Yesterday I'm playing around on OwnCloud, a Dropbox-like system, which is also written in PHP. That was scary. I was almost driven to mad due to recurring permission issues. I did chown and chgrp and chmod again and again, but I was still not satisfied with the result. The primary issue is that Apache server runs as the user www-data, but I hope it has the permission to read and write ONLY the files and directories that I specified for OwnCloud. I've tried apache2-mpm-itk and suPHP, but neither matched my requirements. Other PHP-based systems, like Gallery 3, have similar problems. (Gallery 3 is even worse because the only way to share pictures is uploading. I can't specify a local directory for picture sharing.) These limitations might be due to security concerns. To prevent attacks, most web applications use their own strict account management systems, and WordPress is not an exception. But if they provide an option of integrating local Linux users into these systems, what will occur? The server might be exposed to the threat from invaders, but it's convenient for many personal content providers like me. I know convenience and security can't coexist in most cases, but the problem I faced now is not inconvenience, but not working!&lt;/p&gt;</content><category term="misc"/><category term="from-wordpress"/></entry></feed>