2014/12/07

Just a Note: GCALDaemon 1.0 alpha 17 for Google Calendar API V3

Google 公告於 2014/11/17 停止支援 Calendar API v2,請使用 API v3。
As announced by Google, Google Calendar API v2 is a subject to the Deprecation Policy and was shut down on November 17, 2014. Please use APIv3 instead.

所以便試著將 GCALDaemon 提升至 API v3。
So I try to upgrade Google Calendar API to v3 for GCALDaemon.

我只有針對 Google Calendar 和 Rainlendar 同步處理的部分,修改了兩個功能:
1. config-editor 中的檔案同步設定。
2. sync-now (手動同步)
I only modified two function for synchronizing calendar events between Google Calendar & Rainlendar.
1. config-editor for File synchronizer.
2. sync-now (manually sync)

修改後的程式可以在 github 取得。
The modified source can be download from github.

在 API v3 中,有兩項比較顯著的變更:
1. 授權方式:必須使用 OAuth 2.0,而不支援其他的授權方式。
2. Google 日曆的 private URL:無法透過 API 取得 private URL 了。
For API v3, there are two significant changes:
1. authorize method: Application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
2. calendar private URL: We can't fetch calendar private URL from API anymore.

所以在開始使用新版 GCALDaemon 前,需要進行以下步驟。
So we have to follow steps below to use new GCALDaemon.

1. 從 My Google Drive 下載 GCALDaemon 並解壓縮。
1. Download GCALDaemon from My Google Drive and unzip.

2. 啟用 Google Calendar API。
2. Enable Google Calendar API.
(1) 開啟 Google Develop Console 並新增專案。
(1) Go to Google Develop Console and create a project.

(2) 啟用 Calendar API。
(2) Enable Calendar API.

(3) 新增 Client ID。
(3) Create Client ID.




(4) 下載 JSON,並更名為 client_secrets.json。
(4) Download JSON and rename to client_secrets.json.

(5) 將 client_secrets.json 放到 GCALDaemon/etc 目錄中。
(5) Put client_secrets.json into GCALDaemon/etc folder.

3. 執行 bin/config-editor-new.bat (或  bin/config-editor-new.sh),點選 "File synchronizer"。直接點選 "+New" 新增欲同步的日曆,不需要做 Account 的新增。
3. Execute bin/config-editor-new.bat (or bin/config-editor-new.sh) and click "File synchronizer", Just click "+New" to add Calendar you want to sync. Don't need to add Google Accounts.

4. 點選 "Refresh" 取得 Google Calendar 清單,此時會開啟瀏覽器,並要求授予管理日曆的權限。同意後,便可在下拉選單中看到你的日曆清單。
4. Click "Refresh" to fetch Google Calendar list from your default logged-in Google Account. There will show authorizing page in browser and you should click accept. Then you can get calendar list into combo box.



5. 選擇要同步的日曆,並指定對應的 iCal 檔案路徑。
5. Select the Calendar you want to sync and specify the iCal file location. Click OK.

6. 取得並複製該日曆的 ICAL 私人網址,只要保留 /calendar/ical/xxxxxxx/private-yyyyyyyy/basic.ics 的部分。
6. Open browser to get Google Calendar private URL. Copy the private address of ICAL and only keep the URL like /calendar/ical/xxxxxxx/private-yyyyyyyy/basic.ics

7. 回到 Config Editor 並修改步驟 5 新增的日曆,修改 Google Calendar 欄位,輸入步驟 6 取得的 URL。
7. Back to Config Editor and edit the calendar created on step 5. Change the Google Calender field to /calendar/ical/xxxxxxx/private-yyyyyyyy/basic.ics from step 6.


8. 儲存並離開 Config Edit。
8. Save and Exit config-editor.

9. 執行 bin/sync-now-new.bat (或 bin/sync-now-new.sh ) 進行同步。
9. Now you can execute bin/sync-now-new.bat (or bin/sync-now-new.sh ) to synchronize Google Calendar and the iCal file for Rainlendar.

2014/12/03

Just a Note:不常駐 GCALDaemon 又能自動同步 Google Calendar 和 Rainlendar 的方法

2014/11 Update:
Google 在 2014/11/17 停止對 Calendar API v2 的支援,所以 GCALDaemon 已經無法正確同步 Google Calendar 了。
所以在善心人士修正 GCALDaemon 前,也許只剩下購買 Rainlendar Pro 一途。
如果你真的還想要繼續用 GCALDaemon,可以參考這篇,但是說實話,程序複雜很多......


2014/09/11

Trip:東眼山國家森林遊樂區



中秋前一日,東眼山森林遊樂園。

約莫兩個小時的車程,初次拜訪東眼山。整個森林遊樂區腹地極廣,涵蓋了東眼山、親子峰兩處登山步道,以及較為緩和的東眼山林道、景觀步道、森林知性步道。
因為沒有事先做功課,所以一時間也不知道該怎麼走比較適當,所以便選擇先東眼山林道,打算抵達造林紀念石後,再看狀況評估後續路程。
整個林道起伏不大,但都是碎石路,且正午時分樹蔭又相對少了些,走來有些痛腳且悶熱,所以抵達化石區後便決定折返。
當下評估狀況,覺得不適宜再登高,所以決定捨棄親子峰和東眼山登山步道,計畫循森林知性步道+景觀步道前往餐廳、再到遊客中心。

從化石區折返後,選擇了過了景觀亭的第一個岔路離開林道。高聳參天的柳杉、原始潮濕的步道,景觀與林道迥異。
沿途全無人跡,所幸並無岔路,所以走來倒也輕鬆愉快,直到經過了往親子峰的登山口,遇到了第一批反方向來的遊客。
接著到了一個三岔路口,卻僅標示著親子峰的方向,本想詢問坐在路旁休息的一對男女,最後決定選擇往較多人過來的方向前進。

炙熱的陽光消失,且飄起了細雨。心想著趕快抵達餐廳避雨,順便看看有甚麼能填肚子的。
不知道何時一個著藍色衣服的小男孩超越我們,獨自一個人在步道中前進,且看來神色自若,我回頭望了望,不見其他大人的身影,暗忖著這小朋友怎麼這麼大膽在山中獨自一人,感覺就像他家後院一樣。
小男孩三步併兩步地前進,很快就遠離了我的視線,但過沒多久,便聽到遠處傳來哭聲,並清晰地喊著喊著「我要找爸爸、我爸爸不見了」。
我們加快了步伐,來到了一個四岔路口,看到小男孩蹲在路口不遠處、南面的步道上。正準備過去他身邊時,卻瞥見小男孩突然用餘光看向我們,然後又繼續哭喊著相同的字句。
心中升起一陣違和感,所以便停在路口,喊了他兩聲,小男孩沒搭理,逕自在步道上來回跑著、哭著。
正盤算著該怎麼處理、同時也在研究路線時,剛才坐在路旁休息的男女從後方出現,小男孩也看到他們,並開始往回走。
我以為那就是小男孩的爸媽,但很快便知道不是。小男孩依然哭喊著繞過我們走向那對男女,男的叫他牽著阿姨的手、又問了他一些問題,便說「我們帶你去餐廳,那裡有廣播」。
剛剛還哭天搶地的男孩突然就噤聲乖乖地跟著他們走,而男女也毫不遲疑地往某條草高及腰的岔路前進。

我還在研究該怎麼走。
他們也要去餐廳?跟他們走就好了?看看指標,四岔路只有三個指標,餐廳的指標卻指向兩條岔路的中間?再看看路線示意圖,他們走的方向似乎是往林道的方向?
雖然有很多問號在腦中浮現,但想了想,還是決定按照自己的方向感前進,沿途看到更多的遊客迎面而來,讓我更加確定自己沒走錯。

那對男女和小男孩呢?
抵達餐廳後,稍做等候,並未看見三人,而沿途也沒看到像是在找尋小孩的家長,更不用說一直到離開園區為止,根本沒有聽到任何廣播。
抱著很多疑問離開園區,突然大雨滂沱,躲進了布列塔尼咖啡莊園等雨停,然而沒花費什麼體力且悠閒地享用過咖啡的我,卻在回程時疲憊到不得不停下車在路邊休息後才能再繼續騎車。

我們心中都有許多疑惑,但直到隔天晚上才拿出來討論。
小男孩甚麼時候出現的?我們走的路一直沒岔路,後方也都沒人,遇到的第一批人是和我們反方向的。
原本神色自若、步伐輕快的小男孩,怎麼突然就在路邊放聲大哭?
哭的時候咬字怎麼還能那麼清楚?
「我要找爸爸、我爸爸不見了」,感覺就是要喊給路人聽的......
那對男女走的路線,顯然不是往餐廳的路(雖然可以到,但是比較繞)。

當你想著這小孩怎麼沒人陪,就突然聽到他的哭喊,雖然前一刻他還神色自若......
當你在岔路研究要怎麼到餐廳,突然有人也說要去餐廳,毫不遲疑地就走向其中一條岔路,但卻是方向錯誤且沒有指標的那條......

2014/07/21

Just a Note:Jetty Runner

說明:
Jetty/Howto/Using Jetty Runner

下載:
Jetty Runner Download

看起來只有 for Jetty 7.x 和 8.x 的版本,不過應該已經足夠。

基本的需求是:
1. 把 Web Application 當作 stand alone 的應用程式部署。
2. 檔案愈少愈好。

如此便只需要 jetty runner 的 jar 檔和 application 的 war 檔共兩個檔案,透過 command line 便可以啟動 application,如此對 Application Server 的依賴度就會降低。

啟動程式:

java -jar jetty-runner.jar --path /myapp myapp.war

2014/07/17

Why Do I Choose Redmine for Issue Tracking?

必須先說明,工作以來用過 Mantis、Clear Case、Trac、Redmine 等 Issue Tracking System,Clear Case 要商用軟體就不用談了,真正有實際架設經驗的只有 Trac 和 Redmine,會選用 Redmine 作為主要 Issue Tracking 系統的原因如下,主要還是跟 Trac 進行比較:

1. 原生支援整合 Subversion、Git。
 這段分析對 Trac 比較不公平一些,之前使用 Trac 時,經歷了整合 Subversion 1.4.x、1.5.x、1.6.x 和 Git,每一階段都很痛苦,因為 python 的版本、Trac 版本、相關 plugin 版本的配合著實令人頭痛。後來改用 Redmine 時,Subversion Server 已經固定使用 1.6.x 版,所以只要選用支援的版本,基本上就可以無痛整合;另外隨著 Git 的普及,原生整合 Git 對我來說也是相當重要的功能。我之前同時嘗試用 Trac 和 Redmine 整合 Git,但最後 Trac 沒有試成功,而 Redmine 則是無痛整合。

2. 較完整的使用者/權限管理/設定,使用者亦可自行進行密碼變更。
 之前用 Trac,沒有使用者自行管理密碼的功能(可能需要額外安裝 plugin),使用者的密碼都是管理者設定後便固定不變,其實這樣有點怪。

3. 較完整的專案管理/設定
 原生支援 multiple projects、子專案,透過 Web 管理介面便可進行專案的建立與設定。Trac 則是需要先建立一個 template,每要開一個新的專案,就需要複製實體目錄,再手動進行 config 的修改,操作門檻較高,且基本上 Trac 各專案均為獨立個體,只能透過 Web Server 設定,「假裝」有一個統一的入口。

4. 原生支援 wiki/論壇/行事曆功能
 Trac 原生只有 wiki,論壇、行事曆均需額外安裝 plugin。真的經歷過安裝 Trac plugin 的痛苦,便能體會原生支援的美好。

5. Email 通知功能
 Redmine 原生支援。Trac 大概也是得安裝 plugin 吧......

6. 外部系統可整合 Redmine 的使用者,達到 Single Sign-On 的目的。
 目前已將 Subversion、Git、Jenkins 的登入統一由 Redmine 控管。



當然我的需求和考量的點不見得對每個專案與環境都適用,僅供大家參考。

網路上都說 Trac 架設的門檻高,根據經驗的確如此,當要使用其他 plugin 時,更是痛苦。不過嚴格說起來,其實 Redmine 的架設也不是真的很容易,要感謝 Bitnami 提供相當完整的 Installer,讓這些系統的架設變得更為容易。

2014/06/14

MOOC

最近開始接觸 MOOC(Massive Open Online Course,大規模網路免費公開課程),網路上有很多介紹的文章,我不敢說對每個人都會有顯著的效益,但這就跟在求學的時候一樣,每個人都上了很多課,但不見得獲得了相同的知識,所以還是蠻建議有興趣的人可以研究看看。
我目前只接觸了 Codecademy 和 Coursera,Codecademy 是屬於程式設計的課程,因為是本業,所以想借此看看現在的趨勢;Coursera 的領域則非常廣泛,感覺就很像大學的通識課。由於我只各參與了一門課(還沒修完),所以沒辦法進行評價,但可以分享一下初步接觸 Coursera 的感想。

1. 大多數都是英文課程,透過影片進行授課,影片可以開啟字幕、可以調整速度、可以隨時暫停,所以對於想提升英文能力的人,我相信一定有幫助。
2. 如果是想快速了解課程的內容,影片幾乎都有簡體中文、甚至正體中文的字幕,相信可以提升學習的效率。
3. 很多已經結束的課程都開放隨時選修,只是就不清楚是不是還有做業、考試的互動了。
4. 課程會區分成許多段最多數十分鐘的影片,每週時數加總起來大概也不會超過兩小時,所以如果加上練英聽、做作業,粗估每週花費 12 ~ 24 小時應該就可以完成該週的進度。
5. Android 和 iOS 上都有 Coursera 的 App,影片都可以下載到手機或平板上,可以利用零碎的時間反覆觀看。

附上一些介紹 MOOC 的連結,提供給大家做參考:
http://www.thenewslens.com/post/45399/
http://www.thenewslens.com/post/36571/
http://www.inside.com.tw/2014/06/13/a-taiwanese-developer-thought-about-coursera

2014/06/11

Just a Note:StandardPasswordEncoder 解析

StandardPasswordEncoder 是 Spring Security 提供的 class,僅有兩個 method:encode 和 matches,分別用來對密碼進行編碼及驗證。

StandardPasswordEncoder(CharSequence secret)
String encode(CharSequence rawPassword)
boolean matches(CharSequence rawPassword,String encodedPassword)

編碼的演算法基本上為 Hash,但並非單純的 Hash,其因子有三:
1. Secret:透過 Constructor 傳入,用來和 Raw Password 一起進行 Hash 的資料,通常為有意義的唯一值,例如帳號,以避免不同帳號、相同密碼產生出一模一樣的 Hash 值。
2. Raw Password:密碼明碼。
3. Salt:為一亂數,用來強化密碼編碼。

編碼的基本流程:
0. 使用者註冊時,輸入帳號、密碼。
1. 根據帳號 (Secret) new 出一個 StandardPasswordEncoder 物件。
2. 呼叫 encode,傳入密碼 (Raw Password)。
 (1) 產生 n byte 的 Salt。StandardPasswordEncoder 中會產生 8 bytes 的亂數。同一系統中,Salt 的長度需相同,除非連長度都當作產生 Hash 的因子保存下來。
 (2) 將 (1) 產生的 Salt、1. 傳入的 Secret、2. 傳入的 Raw Password 組合起來一起進行 Hash 運算,產生 Hash。StandardPasswordEncoder 採用 SHA-256 演算法,並針對資料進行 1024 次 Hash 運算。
 (3) 取得 Salt+ Hash (Encoded Password),存入資料庫。

步驟 2. (1) 加入了未知長度、且每次隨機產生的 Salt,即使使用相同帳號 (Secret)、相同密碼 (Raw Password),每次編碼出來的結果也不會相同;
步驟 2. (2) 進行了多次 Hash 演算,最後的結果就不會是以單純的 ASCII 字元產生出的 Hash,而是值域更廣 binary data,可以增加暴力破解的難度。

驗證的基本流程:
0. 使用者登入時,輸入帳號、密碼。
1. 根據帳號 (Secret) new 出一個 StandardPasswordEncoder 物件。
2. 呼叫 matches,傳入密碼 (Raw Password) 和從資料庫取得的 Encoded Password。
 (1) 從 Encoded Password 中取出 Salt 和 Hash。
 (2) 將 (1) 取得的 Salt、1. 傳入的 Secret、2. 傳入的 Raw Password 組合起來一起進行 Hash 運算,產生 Hash'。
 (3) 比對 (2) 產生的 Hash' 和 (1) 取得的 Hash 是否相等。相等則代表密碼驗證成功。


原則上透過 StandardPasswordEncoder 的 encode 出來的結果已經很難透過暴力破解取得使用者密碼了,不過也可以再進一步調整部分編碼的參數,例如:
1. Salt 的長度。
2. Hash 時 Salt、Secret、Raw Password 排列組合。
3. Hash 的演算法和次數。
4. Salt、Hash 的排列組合。
只要在 encode 和 matches 中使用相同的因子,便可以自訂出獨有的密碼編碼機制來強化密碼的保護。

如此一來,即便有內部人員可以直接存取資料庫,也無法取得使用者的密碼。

2014/06/05

詐騙簡訊...

友人分享了一個詐騙簡訊,簡訊說「這是上次聚會的照片」,還附上了一個短網址。

短網址還原後,是個韓國的 IP,話說上次也是韓國的 IP,不知道是不是這個空間比較好租......

網址是個 php,但是居然可以瀏覽目錄(也太不專業了)。
目錄底下有兩個 php、一個 txt、一個 jpg、一個 apk( 一.一+ )。

php 沒辦法下載,會自動 rediret 去下載 apk 檔;
jpg 其實是個 xuite 的網頁,是個聚會的照片,跟簡訊描述一致,所以其實不算詐騙?(是這樣解釋的嗎?)
txt 是 log 檔,記錄了所有點擊簡訊內連結的裝置資訊,例如手機型號、OS版本、瀏覽器版本、IP、點擊的時間等等。附帶一提,這個 log 檔有 203 MB,包含 5/21 到今天的點擊資訊,總共有 63 萬筆......也就是每天有 4 萬多次點擊,其中有非常多人是一而再、再而三地點、不停地點......

apk 是重頭戲啦!馬上拿去反組譯,看看到底寫了甚麼神奇的功能,可以達到詐騙的目的。
基本上是一個在 Android 上可以跟遠端溝通的常駐程式,主要功能如下:
1. 取得此裝置通訊錄。
2. 遠端可偵測此裝置是否上線。
3. 取得此裝置所有收到的簡訊。(重點!)
4. 上傳所有目前裝置內的簡訊。
5. 透過此裝置發簡訊。

所以當使用者點擊了詐騙簡訊的連結、也安裝了 apk 後,這時候小額付款機制就派上用場了......
詐騙集團使用安裝木馬的手機號碼進行小額付費,小額付費會發認證簡訊到該手機上,因為上述 3 的功能,詐騙集團可以立刻拿到授權碼(根本不需要用 line 跟你裝熟、請你幫忙收簡訊......),其實現實中還有很多使用簡訊 OTP 進行認證授權的機制,這時候都變成詐騙集團可以利用的管道。
然後你的荷包就被掏空了......

報告完畢,謝謝收看。

2014/04/13

Android App:Daily Money New Branch for DB Schema Compatibility

This project is a fork of original Daily Money project hosted on github by Dennis Chen

https://github.com/dennischen/daily-money

建立新的分支,在不改變官方資料結構的情況下加入新功能。
所以此分支不包含
1. 金額由 double 轉 BigDecimal 的處理。
2. Tag 功能。

APK can be downloaded in dist folder.


完整功能版本:
Source & Binary:
https://github.com/LancelotLiu/daily-money-1/tree/lancelot
APK can be downloaded in dist folder.

2014/03/07

Just a Note:Sun JCE 驗章問題

驗章時須使用 Public Key,一般來說,在 JCE 下使用已知的 Key 值建立 Public Key 的方式有兩種:

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);

差別在於 Key 值的保存型態,
第一種存的是 Key.getEncoded() 的值 (http://docs.oracle.com/javase/7/docs/api/java/security/Key.html),
第二種保存的是 Public Key 的 modulus 和 exponent。而使用第二種方式時,通常我們亦會把 modulus 和 exponent 用 byte[] 保存。

最近遇到的問題是,
如果 Public Key 是透過 RSAPublicKeySpec 建立的話,會驗章失敗;使用 X509EncodedKeySpec 建立的話,則會成功。
但是把兩種方式建出來的 Public Key dump 出來 (getEncoded()),卻會得到一模一樣的值。

Debug 後發現,
RSAPublicKeySpec 建出來的 Key,其 modulus 是負數,
X509EncodedKeySpec 建出來的 Key,其 modulus 則是正數。

解決方式:
當 modulus 的第一個 byte 大於等於 0x80 時,需在最前面多放一個 byte 0x00,再轉成 BigInteger,即能正確取得 modulus 的值。

String modulusStr = "C868EEE8D1D300207EA65DB1DBBFD52552AE1814E07FDF23DE040EC96E6F0F35C8ECF922C11D7F39B3E3162B96881447D16B0FCC75FB0378F2B19AFB22884B8F2E2840C74FE73DBCDBF653E26070955BB9A4011C1EDD9E1DBA482F02091DBBFAD77D13F91A06FF76C8FDE1E4F475A2DFF8A9567C115F50ACCA573315F7723D333218E266984953D0642057C46D6F952CFC7CFAEAA8945FAEDE441ADA8497F0895C1AC18CE6679467F6EFEF5C741879487E94B8DAF87FA45E87FD011BB0A794B4F433B655959B6D1D8164793421A360A39E17A2287EE5CFE7200FF6FA3F407DD7FEC2EAA6C6A89BE5A3CE3073B758E56654C17CE17A3657E76E219E1BECA9115D";
byte[] modulusTmp = Util.hexStringToByte(modulusStr);
byte[] modulus = null;
if (modulusTmp[0] < 0) {
    modulus = new byte[modulusTmp.length + 1];
    modulus[0] = 0x00;
    System.arraycopy(modulusTmp, 0, modulus, 1, modulusTmp.length);
} else {
    modulus = new byte[modulusTmp.length];
    System.arraycopy(modulusTmp, 0, modulus, 0, modulusTmp.length);
}

2014/01/05

Android App:Daily Money Change Log 2014/01/05

This project is a fork of original Daily Money project hosted on github by Dennis Chen

https://github.com/dennischen/daily-money

added: hide accounts with zero balances in reports

新增:在報表隱藏金額為零的帳戶

(顯示)

(隱藏)