Sunday, October 2, 2011

su binary outdated

第一次试用cm的nightly颁布。su就用问题:
1, 打开su会提示su binary outdated.
2, 用ti备份恢复时,每个程序需要点10下左右才可以完成恢复。总算还可以将就。
为了能够在出游时可以用上自己的手机。我点了600多下,才把64个程序恢复完成,总算hold住了。

回家后,还是想把这个问题解决。试了一些方法都用来更新apk,都不成功。
最后,不知在哪里看到可以到su论坛(http://androidsu.com/superuser/)上下载signed.zip刷机。试一下果然有效。

======================================================
su binary是什么呢?

http://androidsu.com/superuser/
The app(superuser) consists of two parts, Superuser.apk and the su binary.

Superuser.apk runs as any other app and gives you, the user, a place to see what apps you have allowed or denied, as well as view a log of what apps have used su when.

The su binary is what other apps call when they need superuser rights. The binary checks the database maintained by Superuser.apk to determine if you have already granted rights to the requesting app, and if not tells Superuser.apk to display a prompt asking you for permission.

Tuesday, September 20, 2011

我要下船, 观《海上钢琴师》有感

拿钢琴来说
键盘有始亦有终
你确切地知道88个键就在那儿,错不了
它们并不是无限的,而你,才是无限的 。



http://movie.douban.com/subject/1292001/discussion/1115689/

拿编程来说。
就是些语言、语法、类库、技巧,应该是有限的。
我可以事先掌握他们,
然后使用她们,去完成无限任务。

这就是我原来的想法,
我竟然和海上钢琴师的想法如出一辙。

我错了。
在实际过程中,总是需要不断地学习新的知识。
否则就会被时代淘汰。

所以,我要下船,走进更广阔的天地。

Tuesday, August 16, 2011

cygwin用户主目录在哪里?回字有几种写法?

今天,菜鸟我遇到1个有意思的事情:为了要把1个现有的ssh私钥文件复制到用户主目录的.ssh里,需要找到cygwin用户主目录在windows的位置。为此很是出了一身汗。

1,原来的估计是在windows用户目录下,结果不是。下面的文件和cygwin里看到的完全不一样。

2,cygwin下有个home目录,可以下面是空的。不放心,在目录属性里设置隐藏目录可见。还是空的。

3,搏傻!在主目录下创建1个文件hello.txt。用windows search去找。总算找到了。E:\Tool\OpenSSH\home。
原来,之前设置openssh时,在注册表里设置过这个键值。
openssh也是建立在cygwin上,2者的测试可能是通用的,安装cygwin时就引用了这个设置?也许吧。

比较标准的方法如下:

4,用mount命令检查挂接点

$ mount
E:/Tool/OpenSSH/home on /home type ntfs (binary)
I:/cygwin/bin on /usr/bin type ntfs (binary,auto)
I:/cygwin/lib on /usr/lib type ntfs (binary,auto)
I:/cygwin on / type ntfs (binary,auto)
C: on /cygdrive/c type ntfs (binary,posix=0,user,noumount,auto)
D: on /cygdrive/d type ntfs (binary,posix=0,user,noumount,auto)
E: on /cygdrive/e type ntfs (binary,posix=0,user,noumount,auto)
F: on /cygdrive/f type ntfs (binary,posix=0,user,noumount,auto)
I: on /cygdrive/i type ntfs (binary,posix=0,user,noumount,auto)

5,用cygpath命令转换windows和cygwin的目录

$ cygpath -w ~
E:\Tool\OpenSSH\home\***


=============================================
参考文档:
3.3.3 Cygwin 的配置和使用





Wednesday, June 22, 2011

安能饭否笔记3-提取weibo包

大侠说的不错,还是比较简单的。

先导入http,fanfou, data包,会在如下问题报错:
1,对于com.ch_linghu.fanfoudroid.TwitterApplication.DEBUG的引用。
2,对于string.xml的引用。
3,fanfou包中个别模块会用到数据库。
4, 上述包还会引用业务无关的基础代码,如util。

我的处理是:
1,新造一个com.ch_linghu.fanfoudroid.TwitterApplication,用来保存需要的常量。

2,在相关模块里简单地修改代码,转向引用新的TwitterApplication。
//import com.ch_linghu.fanfoudroid.R;
import com.ch_linghu.fanfoudroid.TwitterApplication;
// sb.append(R.string.pref_rt_prefix_default + ":@");
sb.append(TwitterApplication.pref_rt_prefix_default + ":@");

3,对于个别模块里遇到数据库相关代码,注释掉就可以了。
比如在com.ch_linghu.fanfoudroid.fanfou.User中,
//import com.ch_linghu.fanfoudroid.db.MessageTable;
//import com.ch_linghu.fanfoudroid.db.TwitterDatabase;
//import com.ch_linghu.fanfoudroid.db.UserInfoTable;
/* 
    //TODO:增加从游标解析User的方法,用于和data里User转换一条数据
    public static User parseUser(Cursor cursor){

4,完整地引入util包,注意在util包里也会引用string.xml。

5,最终TwitterApplication很简单,
public class TwitterApplication 
{
public final static boolean DEBUG = true; // Configuration.getDebug();
public final static String pref_rt_prefix_default = "热饭";
public final static String tweet_source_prefix = "来自";
public final static String tweet_reply_to_prefix = "给";
public final static String tweet_reply_to_suffix = "的回复";
public final static String tweet_created_at_beautify_prefix = "大约";
public final static String tweet_created_at_beautify_sec = "秒";
public final static String tweet_created_at_beautify_min = "分钟";
public final static String tweet_created_at_beautify_hour = "小时";
public final static String tweet_created_at_beautify_day = "天";
public final static String tweet_created_at_beautify_suffix = "前";
public final static String pref_photo_preview_type_thumbnail = "缩略图尺寸";
public final static String pref_photo_preview_type_middle = "小尺寸";
public final static String pref_photo_preview_type_original = "大尺寸";

一点感想:
仅从代码复用的考虑出发,
1,问题集中在字符串资源上。
   fanfou包是否可以有自己的字符串资源?
   util、http包里string.xml相关的代码是否可以集中到fanfou包里?
2,fanfou包是否可以和数据库无关?
3, 正因为如此的简单,如果再进一步,拿过来就可以用,就好了。

=======================================================================

备注:
导入代码时遇到注释乱码的问题,解决如下:

MyEclipse6.5 注释乱码

打开Window -> preference, 左边 General -> Content Types, 然后在右边上面的框中打开Text, 选中Java Source File (你看到下面的框中有个*.java 就对了), 然后在下面的“Default edcodng”文本框中输入“UTF-8”, 点“Update”,就OK了。(什么文件的编码都可以在这里设置!)
对不起,忘了保留参考url。

Friday, June 17, 2011

安能饭否笔记2-请求响应

想要借用安能的weibo、http代码新建一个项目,学习测试fanfou api。做下去才知道这2个包的并非完全独立,对应用层模块有不小的依赖性。

先想闭着眼睛做加法,结果要加入的模块越来越多。看来改代码是必然的啦。
再想做减法,也没有完全的把握,不如退而结网,把代码看明白。看了代码以后,确定做减法是可行的。

== 以最简单的login为例,从task出发
com.ch_linghu.fanfoudroid.LoginActivity.LoginTask._doInBackground(TaskParams...)
    user= TwitterApplication.mApi.login(username, password);


== 进入weibo模块
com.ch_linghu.fanfoudroid.fanfou.Weibo.login(String, String)
    http.setCredentials(username, password);// 保存id/password到httpclient
    User user = verifyCredentials();

// 发送请求,返回User对象
com.ch_linghu.fanfoudroid.fanfou.Weibo.verifyCredentials()
        return new User(get(getBaseURL() + "account/verify_credentials.json"
                , true).asJSONObject());
get的输入参数就是fanfou检验用户名密码是否正确的路径:
http://api.fanfou.com/account/verify_credentials.[json|xml]

// 调用httpClient.Get 返回respond
com.ch_linghu.fanfoudroid.fanfou.Weibo.get(String, boolean)
       return get(url, null, authenticate);
com.ch_linghu.fanfoudroid.fanfou.Weibo.get(String, ArrayList<BasicNameValuePair>, boolean)
return http.get(url, authenticated);


== 进入httpClient:req/rsp
// 初始化,对于http还有不少不明白,只是简单地概述了一下
public HttpClient() {
        prepareHttpClient();
// 关于连接        
设置最大连接数 10
设置http版本 1.1
设置http:80,https:443
把上述设置保存在param里,创建DefaultHttpClient
mClient = new DefaultHttpClient(cm, params);      
支持gzip

// 关于认证
设置basic auth
mAuthScope
(m)localcontext


// Interceptor==拦截,preemptive=先发制人
mClient.addRequestInterceptor(preemptiveAuth, 0);
private static HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor()
用来预处理处理http的request?



// 发送指令
com.ch_linghu.fanfoudroid.http.HttpClient.get(String, boolean)
        return httpRequest(url, null, authenticated, HttpGet.METHOD_NAME);


com.ch_linghu.fanfoudroid.http.HttpClient.httpRequest(String, ArrayList<BasicNameValuePair>, File, boolean, String)
  // Create POST, GET or DELETE METHOD
        method = createMethod(httpMethod, uri, file, postParams);
        // Setup ConnectionParams, Request Headers
        SetupHTTPConnectionParams(method);
        // Execute Request
       // 因为是同步协议,现在响应已经收到
        response = mClient.execute(method, localcontext);
        res = new Response(response);
        
        if (response != null) {
            int statusCode = response.getStatusLine().getStatusCode(); // 处理状态 当响应码不为200时都会报出此异常:
            // It will throw a weiboException while status code is not 200
            HandleResponseStatusCode(statusCode, res);
    // 返回respond     
            return res;


== 通用respond(json) ---> 业务数据(user)
// 发送请求
com.ch_linghu.fanfoudroid.fanfou.Weibo.verifyCredentials()
        return new User(get(getBaseURL() + "account/verify_credentials.json"
                , true).asJSONObject());
                
com.ch_linghu.fanfoudroid.fanfou.User.User(JSONObject)
init(json);

com.ch_linghu.fanfoudroid.fanfou.User.init(JSONObject)
id = json.getString("id");
    name = json.getString("name");                
    .....

=================================================================================

再看看业务数据怎么显示到界面上?
== 以首页TweetActivity为例,还是从Task出发,发送请求
com.ch_linghu.fanfoudroid.ui.base.TwitterCursorBaseActivity.RetrieveTask._doInBackground(TaskParams...)
    String maxId = fetchMaxId(); 
    statusList = getMessageSinceId(maxId);
上面2个方法都是abstract

取出当前页(TYPE_HOME)的最大id
com.ch_linghu.fanfoudroid.TwitterActivity.fetchMaxId()
    return getDb().fetchMaxTweetId(getUserId(), StatusTable.TYPE_HOME);

已知消息id,查询时间线
com.ch_linghu.fanfoudroid.TwitterActivity.getMessageSinceId(String)    
    return getApi().getFriendsTimeline(new Paging(maxId));
    return getApi().getFriendsTimeline();

访问weibo, 返回List<Status>
com.ch_linghu.fanfoudroid.fanfou.Weibo.getFriendsTimeline(Paging)
    return Status.constructStatuses(get(getBaseURL() + "statuses/friends_timeline.json",null, paging, true));


== Task处理响应
// List<Status> ===〉ArrayList<Tweet>

com.ch_linghu.fanfoudroid.ui.base.TwitterCursorBaseActivity.RetrieveTask._doInBackground(TaskParams...)
    ArrayList<Tweet> tweets = new ArrayList<Tweet>();
    for (com.ch_linghu.fanfoudroid.fanfou.Status status : statusList)
    tweets.add(Tweet.create(status));
    mRetrieveCount = addMessages(tweets, false); // 又是abstract


// ArrayList<Tweet> ===〉dbcom.ch_linghu.fanfoudroid.TwitterActivity.addMessages(ArrayList<Tweet>, boolean)
for (Tweet t : tweets)
            getDb().createWeiboUserInfo(t.user);
    return getDb().putTweets(tweets, getUserId(), StatusTable.TYPE_HOME, isUnread);      

后续应该会同步db和listView吧?

Friday, June 10, 2011

安能饭否笔记1-初始化

拿到代码总想找到入口,然后慢慢看下去。不知道是不是一个好方法?

1,从AndroidManifest.xml开始,
<application android:icon="@drawable/icon" android:label="@string/app_name" android:name="TwitterApplication">
找到Application模块,com.ch_linghu.fanfoudroid.TwitterApplication
这里保存了很多全局的对象,如数据库,微博接口,图片加载......
还要为cmwap用户设置代理上网,难怪程序的设置里要说明网络类型。

2,再找启动的activity

<activity android:name=".TwitterActivity"
    <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>

应该是com.ch_linghu.fanfoudroid.TwitterActivity

3,找布局文件,怎么没有onCreate, setContentView?
其实,我已经花了不少力气找发送查询指令的地方,所以知道显示推的ListActivity有1个很深的类层次。
直到com.ch_linghu.fanfoudroid.ui.base.BaseActivity才找到onCreate。
在onCreate里只是调用了_onCreate(savedInstanceState);  所以,可以把_onCreate看作onCreate。
为什么要用_onCreate呢?

    // 因为onCreate方法无法返回状态,因此无法进行状态判断,
    // 为了能对上层返回的信息进行判断处理,我们使用_onCreate代替真正的
    // onCreate进行工作。onCreate仅在顶层调用_onCreate。
    protected boolean _onCreate(Bundle savedInstanceState) 

com.ch_linghu.fanfoudroid.TwitterActivity._onCreate(Bundle)里没有setContentView,
Search setContentView,找到了。
com.ch_linghu.fanfoudroid.ui.base.TwitterListBaseActivity._onCreate(Bundle)
   setContentView(getLayoutId());
getLayoutId应该是1个抽象函数
com.ch_linghu.fanfoudroid.ui.base.TwitterCursorBaseActivity.getLayoutId()
   return R.layout.main; 
所以,所有List页面使用相同的layout。

main.xml里嵌套了1个工具条的布局,又学了1招

<!-- Header -->
    <include layout="@layout/header"/>

4,想知道发送指令的过程?
去找工具栏里的“刷新”按钮。
List页面的工具栏在这里,com.ch_linghu.fanfoudroid.ui.base.TwitterListBaseActivity.mNavbar
所以,List页面的navibar都是相同的模块。

为“刷新”按钮设置处理方法的代码在这里:
com.ch_linghu.fanfoudroid.ui.module.NavBar.addRefreshButtonTo(Activity)
public void onClick(View v) {
((Refreshable) activity).doRetrieve();

doRetrieve是1个接口方法,具体实现在:
com.ch_linghu.fanfoudroid.ui.base.TwitterCursorBaseActivity.doRetrieve()
mRetrieveTask = new RetrieveTask();

在RetriveTask里应该发送指令了
com.ch_linghu.fanfoudroid.ui.base.TwitterCursorBaseActivity.RetrieveTask._doInBackground(TaskParams...)
   statusList = getMessageSinceId(maxId);

getMessageSinceId又是1个abstract.
com.ch_linghu.fanfoudroid.TwitterActivity.getMoreMessageFromId(String)里总算看到了api
   return getApi().getFriendsTimeline(paging);

5,收数据的过程:
    以后学习ListActivity类层次的时候再看。

6,注销登入后,再次打开程序,为什么会显示登入页面?
    不是在Application里,就在Activity基础类里
    Application里没有,就是从Acitivity发initent消息过来。
    Search “LoginActivity”, 找到了发送intent的地方。
    com.ch_linghu.fanfoudroid.ui.base.BaseActivity.showLogin()
        Intent intent = new Intent(this, LoginActivity.class);
    继续向上找
    com.ch_linghu.fanfoudroid.ui.base.BaseActivity.handleLoggedOut()

    com.ch_linghu.fanfoudroid.ui.base.BaseActivity.checkIsLogedIn()
    com.ch_linghu.fanfoudroid.ui.base.BaseActivity._onCreate(Bundle)
    各大Activity的onResume里也都有checkIsLogedIn
    所以无论打开哪个页面,都有可能回到登入页面。

=================================================================
一些感想:
1,程序使用了很深的继承,然后依靠接口、抽象函数来具体化特定的功能。
     所以在上述的文字里会经常看到:“所有......”, "相同.......", "是1个抽象函数.............."
     继承是一种非常强的偶合, 如果过多地使用,是否会限制后续层次的扩展?
     是否可以适当的使用设计模式,使得类层次扁平化?

2,在学习代码的过程中,一直在纠结为什么不把LoginActivity作为laucher?
     一部分原因是,一直在做的程序每次打开都需要手工验证,而且采用的是tcp长连接。和安能的差别非常大,一下子没有适应过来。
     更多的原因是,对于android程序生命周期、http协议还不熟悉,需要以后不断体会。

Wednesday, June 8, 2011

android ApiDemos笔记1 - 运行时遍历activity

关于启动activity: com.example.android.apis

在运行时,递归遍历属下所有的activity (queryIntentActivities)
过滤出和指定前缀相关的activity
归纳所有activity lable路径的第1层目录,构造1个list

list元素的title = activity lable(优先)或者activity的pack路径,
list元素的object = 启动activity的intent

如果title是activity lable,使用browseIntent创建intent
如果title是activity的pack路径,使用activityIntent创建

通过 SimpleAdapter,把list中的title显示到listActivity上。
选中listActivity中的项目,找到list只能给的intent,触发intent

2个activity的处理过程,
06-08 02:49:52.451: DEBUG/getData(382): ===> i = 0
06-08 02:49:52.511: DEBUG/getData(382): labelSeq = App/Activity/Hello World
06-08 02:49:52.521: DEBUG/getData(382): info.activityInfo.name = com.example.android.apis.app.HelloWorld
06-08 02:49:52.521: DEBUG/getData(382): label = App/Activity/Hello World
06-08 02:49:52.531: DEBUG/getData(382): prefix = 
06-08 02:49:52.531: DEBUG/getData(382): if (prefix.length() == 0 || label.startsWith(prefix))
06-08 02:49:52.531: DEBUG/getData(382): nextLabel = App
06-08 02:49:52.542: DEBUG/getData(382): prefixPath = null
06-08 02:49:52.542: DEBUG/getData(382): labelPath = [Ljava.lang.String;@40599058
06-08 02:49:52.550: DEBUG/getData(382): else if (entries.get(nextLabel) == null)
06-08 02:49:52.561: DEBUG/getData(382): browseIntent(App)


06-08 02:49:53.880: DEBUG/getData(382): ===> i = 58
06-08 02:49:53.880: DEBUG/getData(382): labelSeq = Views/Layouts/RelativeLayout/2. Simple Form
06-08 02:49:53.880: DEBUG/getData(382): info.activityInfo.name = com.example.android.apis.view.RelativeLayout2
06-08 02:49:53.880: DEBUG/getData(382): label = Views/Layouts/RelativeLayout/2. Simple Form
06-08 02:49:53.880: DEBUG/getData(382): prefix = 
06-08 02:49:53.880: DEBUG/getData(382): if (prefix.length() == 0 || label.startsWith(prefix))
06-08 02:49:53.880: DEBUG/getData(382): nextLabel = Views
06-08 02:49:53.880: DEBUG/getData(382): prefixPath = null
06-08 02:49:53.880: DEBUG/getData(382): labelPath = [Ljava.lang.String;@405270c8


2套Activity目录

06-08 02:05:50.591: DEBUG/getData(346): label = Content/Storage/External Storage
06-08 02:05:50.591: DEBUG/getData(346): nextLabel = Content
06-08 02:05:50.661: DEBUG/getData(346): label = Content/Resources/Styled Text
06-08 02:05:50.661: DEBUG/getData(346): nextLabel = Content
06-08 02:05:50.661: DEBUG/getData(346): label = Content/Assets/Read Asset
06-08 02:05:50.661: DEBUG/getData(346): nextLabel = Content
06-08 02:05:50.692: DEBUG/getData(346): label = Content/Resources/Resources
06-08 02:05:50.692: DEBUG/getData(346): nextLabel = Content
06-08 02:05:50.692: DEBUG/getData(346): label = Content/Provider/Pick Contact
06-08 02:05:50.692: DEBUG/getData(346): nextLabel = Content


06-08 02:05:50.692: DEBUG/getData(346): label = OS/Morse Code
06-08 02:05:50.692: DEBUG/getData(346): nextLabel = OS
06-08 02:05:50.741: DEBUG/getData(346): label = OS/Sensors
06-08 02:05:50.741: DEBUG/getData(346): nextLabel = OS
06-08 02:05:50.741: DEBUG/getData(346): label = OS/Rotation Vector
06-08 02:05:50.741: DEBUG/getData(346): nextLabel = OS
06-08 02:05:50.780: DEBUG/getData(346): label = OS/SMS Messaging
06-08 02:05:50.780: DEBUG/getData(346): nextLabel = OS

=============================================================================
后续层次的activity列表也都采用ApiDemos这个Activity

在创建activity中,获得当前activity的lable路径
public void onCreate(Bundle savedInstanceState)
   String path = intent.getStringExtra("com.example.android.apis.Path");

在创建ListActivity项目中,每个项目对应1个新的Activity。
如果新的activity还是1个列表Activity。
构建intent时,需要保存这个activity的lable路径
protected Intent browseIntent(String path)
   Intent result = new Intent();
   result.setClass(this, ApiDemos.class);
   result.putExtra("com.example.android.apis.Path", path);

如果不是列表Activity,就是最底层的具体的功能activity了。
构建Intent是保存包名和其他Activity类型
protected Intent activityIntent(String pkg, String componentName) {
        Intent result = new Intent();
        result.setClassName(pkg, componentName);

触发intent,显示Intent指定的Activity类型。

连续深入列表Activity的过程如下:
06-08 05:56:48.590: DEBUG/ListItemClick(514): path = intent.getStringExtra = 
06-08 05:56:49.330: DEBUG/ListItemClick(514): intent.putExtra = App
06-08 05:56:50.380: DEBUG/ListItemClick(514): intent.putExtra = Content
06-08 05:56:50.530: DEBUG/ListItemClick(514): intent.putExtra = OS
06-08 05:56:50.540: DEBUG/ListItemClick(514): intent.putExtra = Views
06-08 05:56:52.381: DEBUG/ListItemClick(514): intent.putExtra = Graphics
06-08 05:56:53.271: DEBUG/ListItemClick(514): intent.putExtra = Media
06-08 05:56:53.281: DEBUG/ListItemClick(514): intent.putExtra = Text
06-08 05:56:53.331: DEBUG/ListItemClick(514): intent.putExtra = NFC

06-08 05:58:16.340: DEBUG/ListItemClick(514): position, id, title = 0, 0, App
06-08 05:58:16.440: DEBUG/ListItemClick(514): path = intent.getStringExtra = App
06-08 05:58:17.321: DEBUG/ListItemClick(514): intent.putExtra = App/Activity
06-08 05:58:17.780: DEBUG/ListItemClick(514): intent.putExtra = App/Service
06-08 05:58:17.910: DEBUG/ListItemClick(514): intent.putExtra = App/Alarm
06-08 05:58:17.920: DEBUG/ListItemClick(514): intent.putExtra = App/Notification
06-08 05:58:18.020: DEBUG/ListItemClick(514): intent.putExtra = App/Search
06-08 05:58:18.060: DEBUG/ListItemClick(514): intent.putExtra = App/Menu
06-08 05:58:18.070: DEBUG/ListItemClick(514): intent.putExtra = App/Preferences

06-08 05:58:27.281: DEBUG/ListItemClick(514): position, id, title = 0, 0, Activity
06-08 05:58:27.371: DEBUG/ListItemClick(514): path = intent.getStringExtra = App/Activity

=============================================================================
1,运行时分析的功能很不错,代码用来处理统一的数据结构,更加简洁。
   ApiDemos专门用来列举下一层activity的目录清单。

2,原来以为ApiDemos只是1个启动用的Activity,没想到功能这么强大。
   接下来,只要看每个具体功能的Activity就可以了。
   幸亏没有把他放一放。

3,intent是一个命令对象,包括需要创建的activity的类型,还可以附带一些命令参数。

Sunday, June 5, 2011

在ubuntu中使用ovpn文件设置openvpn

先简单说个结果:

安装openvpn

sudo apt-get install network-manager-openvpn openvpn


如果你有1个openvpn配置文件,是ovpn形式的,在ubuntu下怎么导入这些设置呢?
1,把ovpn文件改名为conf文件
2,提取其中的证书文件,如 ca.crt, user.crt, private.key,
     比如ovpn文件中保存为:
 <ca>
-----BEGIN CERTIFICATE-----
MIIDijCCAvOgAwIBAgIJAMNr50wqSaM7MA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEjMCEG
......
MIKdTHYQotNz0MHtOTIyJKBDqzNikGVes8F0yfnOSOsuoBJzMwbmVAKKPUxExys4
Xz9s2a2wboOb+NfrKbkOsBrL8EzytrZ8SELDF8cOPqLc8P/5/v8dd3zI/vKTNw==
-----END CERTIFICATE-----
</ca>    
    在ca.crt文件中保存为:
-----BEGIN CERTIFICATE-----
MIIDijCCAvOgAwIBAgIJAMNr50wqSaM7MA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEjMCEG
......
MIKdTHYQotNz0MHtOTIyJKBDqzNikGVes8F0yfnOSOsuoBJzMwbmVAKKPUxExys4
Xz9s2a2wboOb+NfrKbkOsBrL8EzytrZ8SELDF8cOPqLc8P/5/v8dd3zI/vKTNw==
-----END CERTIFICATE-----

3,在conf文件中,使用上述文件的位置描述替代原有的配置段落,比如
ca /home/wang/openvpn.cfg/ca.crt
cert /home/wang/openvpn.cfg/user.crt
key /home/wang/openvpn.cfg/private.key
tls-auth /home/wang/openvpn.cfg/ta.key

4,把conf文件导入vpn设置界面后,就是基本完成了设置。不过还要手工补充一些设置, 比如:采用“带有证书的密码(TLS)”后,可以输入帐号、密码。
不要写私钥密码,其实从ovpn文件中也看不出来。
 高级-TLS认证中,使用附加TLS认证,密钥方向无。

现在至少可以使用openvpn了。
应该还有更合理的方式,  以后再请教也来得及。


 =================================================
参考资料:

Ubuntu 10.04 OpenVPN



Ubuntu 10.04 OpenVPN by StrongVPN tutorial


再回顾一下摸索的过程:

1,按照参考资料设置,结果报错。当时的疑问集中在TLS相关设置上,如TLS密码,加密方向。 
2,导出错误的设置,想看看和ovpn设置的差别在哪里?差别在于使用内嵌方式还是外接方式保存各种证书和密钥 。
3,修改ovpn文件,修改后缀为conf,采用外接方式保存证书和密钥 。
4,导入修改后的conf文件,基本就对了。

Friday, May 27, 2011

在实践中了解bitcoin

最近,微博上开始讨论btc。第一次听说这个名词是在@authur369的推里:我昨天给一老外抠了一张硬件图,挣了1 bitcoin,很得意。我更喜欢交易方式而不是挖矿。(矿让男人挖)。


很奇怪,如果你开始关心这件事情,相关的资料就开始向你走来。

Bitcoin,虚拟世界的黄金
讲述了现实货币的发展历史,在和现实货币的对比中,体现btc特有的属性。
看完以后,我的初步印象是:btc就像早期的金属货币,或者更早的一般等价物,如贝壳。大家可以到海滩上自己去搜索,然后拿到市场上去交换商品。


Bitcoin:Geek们的货币实验

Bitcoin是一种以RSA算法加密的虚拟P2P货币。由于btc增长规模有限,虽然可以抑制通货膨胀,但是也无法满足日益增长的交易需求,所以无法承担现代货币的责任。

实践btc
俺也来些btc吧。于是下载了客户端加入挖矿大军。程序开起来后,看见有1个adress,是不是可以看作我的身份帐号。在网络连接下,block的数量不断增加。于是忘了btc的p2p属性,在第2天就说了无知的言论。

今天看到大侠也发表了文章。【翻译】Bitcoin是一个骗局
是一篇译文,文字很通顺,很有阮一峰的风格。文中力举了btc的种种不是。其中说道btc丢失的问题。我提出,可以把address备份起来。回家路上,我想起btc是在p2p网络上生成的,因为没有中心服务器,光备份一个address恐怕不行。这下说错话了。
回家后,在家里重装btc客户端,真的没有办法把备份的address复制到新的客户端。真的糗了。

btc真的不能备份吗?请看保护你的钱包
把数据目录复制到新的客户端。新的客户端使用原有的address继续创建btc了。

接下来,我还想要什么呢?
1,我想知道,在没有中心服务器的p2p环境下,btc是怎么运作的?怎么有控制地生成?怎么可靠地交易?
2,我想看看自己是否有幸制造出1个btc?
3,我还想和别人做一轮支付交易。
4,我再想在我的手机上使用btc。market上已经有了相关的客户端,等有钱了去试试看。

最后,作为1个非经济人士说一下我对btc的看法:
我认为:btc还处于一般等价物的初级阶段。虽然比现实的货币更加保值,但也存在不少问题。
目前,btc由于其本身特性,决定了btc无法在现实社会中作为巨量商品的流通媒介,更不用说起到促进生产的作用。
以后,btc继续进化,可能又会回到了现有的货币制度。因为目前的制度已经是深思熟虑,平衡了各方利益,长期发展得来的。
也许需要革新目标的可能不是1个货币制度,而是人类的发展模式。相信人类的智慧能够创造更美好的未来。

附录:
我的btc address: 1KiE1r6dWLtFeC9GEnCSUuUHgGVAQR3dwd
如果给我汇钱,我一定如数奉还。我只想体验一下btc的快感,呵呵。

一些参考资料

Bitcoin 中文版wiki

Bitcoin 英文版wiki,内有更详细的原理介绍。

http://www.bitecoin.com/,1个全面的中文网站。
Bitcoin 的基本原理,风云的文章,里面提到了如何避免一笔钱被花两次?





Friday, April 29, 2011

设置android开发环境

如何设置android开发环境?
官方网站已经写得很好了,http://developer.android.com/sdk/index.html,先看download,再看installing the sdk。顺着往下看基本没有什么跳转。
如果没有耐心看e问,可以参考1个视频教材。http://www.mars-droid.com/。很赞作者的奉献精神,每每深更半夜,还在声音洪亮地录视频。可见天才来自勤奋。同时有机会一定要为他多加宣传。

这个过程下来都还算顺利,只是在启动虚拟机的时候报错:emulator: ERROR: unknown virtual device name: '2.1'
stackoverflow上有针对的解决方案:Android emulator reports unknown virtual device
我用了方案2:不把.android目录放在用户目录下,然后设置路径ANDROID_SDK_HOME指向新的位置。
要说什么原因:很可能是因为我在c:\的用户目录不是真实的目录,已经是1个硬连接,导致android虚拟机水土不服。

最后还有1个小问题,不影响使用,就是有点不爽,有空请教一下大侠。
在eclipse中,第一次启动android sdk and avd manager的时候,总是要提示:location of the android sdk has not been setup in the perference。以后再去启动就完全正常了,avd也可以正常运行。

Thursday, April 28, 2011

RSA学习小结(更新)

在实际应用中,发现上次贴出来的测试代码里由不少问题,特此更新。
问题集中在:
1,怎么设置密钥相关变量mpuint的长度?
2,怎么设置明文、密文变量mpuint的长度?

原文是这么写的:
The variables d, e and n must be the same length, which must be an even multiple of 16 bits.
The source must be less than the modulus n(解密时,密文也应该小于n)

如果需要明文的ascii最大长度是64,

unsigned iMaxTextLen = 64;
unsigned iMaxKeyLen = (iMaxTextLen/2+2);
把64个byte换算成word。再加几个word,避免按照字符串输出时没有空结尾。
在加密解密过程中,所需mpuint的长度都可以初始化为iMaxKeyLen。

如果对数学原理很清楚的话,相信对上述问题就一目了然了。


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
还有半小时下班,抓紧。
========================================================
RSA库:http://www.efgh.com/software/rsa.htm
选择理由:开源,简单明白。大侠推荐,品质保证。

========================================================
参考资料:
RSA库可选方案:RSA encryption library for c++,http://stackoverflow.com/questions/108518/rsa-encryption-library-for-c
RSA演示:http://people.eku.edu/styere/Encrypt/RSAdemo.html#genp
RSA加密演算法,http://zh.wikipedia.org/zh/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95
同余,http://zh.wikipedia.org/wiki/%E5%90%8C%E4%BD%99
互质,http://zh.wikipedia.org/wiki/%E4%BA%92%E8%B3%AA
欧拉函数,http://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%87%BD%E6%95%B0

========================================================
测试代码:
注意:需要定义rsa库的外部函数

void numeric_overflow(void)
{
perror( "numeric overflow" );
abort();
}


#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <RsaLib\RSA.H>

int _tmain(int argc, _TCHAR* argv[])
{
printf("== mpuint功能测试 ===========================================\r\n");
mpuint i(2);
memset(i.value, 0, sizeof(*i.value)*i.length);

// mpuint单位长度:2个字节,1个unicode
printf("unsigned short.sizeof ===> %d\r\n", sizeof(unsigned short));
// mpuint总体长度:2个单位,4个字节,2个unicoder
printf("i.sizeof ===> %d\r\n", i.length * sizeof(*i.value));

{
printf("== 逐个单位的输入\r\n");
// 赋值
unsigned short *pValue = i.value;
pValue = i.value;
*pValue = 1;
pValue++;
*pValue = 3;

// 按mpuint单位输出
pValue = i.value;
for (size_t j=0; j<i.length; j++)
{
printf("i[%d] = %d\r\n", j, *pValue);
pValue++;
}

// edit:按照10进制输出到字符串
char szI[1024];
printf("i.edit ===> %s\r\n", i.edit(szI));
long l = atol(szI);
printf("i.edit.printf.0x ===> %0x\r\n", l);

// dump:按照mpuint.value[i]逐个输出到控制台
// 低位单元先输出,高位单元后输出
printf("i.dump ===>"); i.dump();

// scan: 用10进制形式的字符串给mpuint赋值
mpuint s(i.length);
const char* pszI = szI;
s.scan(pszI);
printf("i.edit.scan.dump ===>"); s.dump();
}

{
printf("== 输入字符串\r\n");
// 赋值
char szSource[] = {"ab"};
const char* pszSource = szSource;
memset(i.value, 0, i.length*sizeof(*i.value));
memcpy(i.value, szSource, strlen(szSource));
unsigned short *pValue = i.value;

// 按mpuint单位输出
pValue = i.value;
for (size_t j=0; j<i.length; j++)
{
printf("i[%d] = %d\r\n", j, *pValue);
pValue++;
}

// edit:按照10进制输出到字符串
char szI[1024];
printf("i.edit ===> %s\r\n", i.edit(szI));
long l = atol(szI);
printf("i.edit.printf.0x ===> %0x\r\n", l);

// dump:按照mpuint.value[i]逐个输出到控制台
// 低位单元先输出,高位单元后输出
printf("i.dump ===>"); i.dump();

// scan: 用10进制形式的字符串给mpuint赋值
mpuint s(i.length);
const char* pszI = szI;
s.scan(pszI);
printf("i.edit.scan.dump ===>"); s.dump();
}

printf("\r\n");
printf("== RSA功能测试 ==============================================\r\n");
// 最大(ascii)明文长度
unsigned iMaxTextLen = 64; // 521bit
unsigned iMaxKeyLen = (iMaxTextLen/2+2);

// 已知字符串形式的明文szSource
//
/* 超长 char szSource[] = {"1234567890123456789012345678901234567890123456789012345678901234_"}; */
char szSource[] = {"1234567890123456789012345678901234567890123456789012345678901234"};
// char szSource[] = {"HYTE7M1K4K9U4CW1_HYTE7M1K4K9U4CW1_HYTE7M1K4K9U4CW1"};
// char szSource[] = {"HYTE7M1K4K9U4CW1"};
// char szSource[] = {"HYTE7M1"};
unsigned iTextLen = (unsigned)strlen(szSource);
printf("szSource ===> %s\r\n", szSource);

// 生成密钥
// 注意:den必须长度一致, 而且是16bit的倍数
mpuint d(iMaxKeyLen); // 私钥.exponent
mpuint e(iMaxKeyLen); // 公钥.exponent
mpuint n(iMaxKeyLen); // moudle
/* 从字符串导入 */

char szD[] = "1366959476314201320612104229499274776898462169097826485636934\
0755196674896840342671642725095343222587647553252671687501273973225252986923047\
686286753753292800187223";
char szE[] = "7364464467100835015079598280779551627848613277390947205308161\
0017571755159810471970615094708039828625362188996500904870853267420683968471425\
23360654194537840151975";
char szN[] = "2551046374903721556165087556621364411613628475298617149406045\
5273705136007088455713120641125241943821666337305259447948524285690486822556554\
630571721143649549902363";
const char* pszScan;
d.scan(pszScan = szD);
e.scan(pszScan = szE);
n.scan(pszScan = szN);

/* 动态生成 */
// GenerateKeys(d, e, n);

char szKey[1024];
printf("d ===> "); d.dump(); printf("%s\r\n", d.edit(szKey));
printf("e ===> "); e.dump(); printf("%s\r\n", e.edit(szKey));
printf("n ===> "); n.dump(); printf("%s\r\n", n.edit(szKey));

// 加密:输入字符串形式的明文szSource和公钥pk, 输出字符串形式的密文szResult_Encrypt
// 注意:source必须小于n
mpuint source(iMaxKeyLen);
memset(source.value, 0, source.length*sizeof(*source.value));
memcpy(source.value, szSource, strlen(szSource));
printf("source ===> "); source.dump();
if (!(source < n))
{
printf("!!! assert ===> when encrypt, source < n. \r\n");
}

mpuint result_Encrypt(iMaxKeyLen);
EncryptDecrypt(result_Encrypt, source, e, n);
printf("result_Encrypt ===> "); result_Encrypt.dump();

char szResult_Encrypt[1024];
printf("%s\r\n", result_Encrypt.edit(szResult_Encrypt));

// 解密:输入字符串显示的密文szResult_Encrypt和私钥sk,输出字符串显示的明文
if (!(result_Encrypt < n)) // 注意:source必须小于n
{
printf("!!! assert ===> when decrypt, result_Encrypt < n. \r\n");
}

mpuint result_Decrypt_In(iMaxKeyLen);
const char* pszResult_EnCrypt = szResult_Encrypt;
result_Decrypt_In.scan(pszResult_EnCrypt);

mpuint result_Decrypt(iMaxKeyLen);
EncryptDecrypt(result_Decrypt, result_Decrypt_In, d, n);

printf("result_Decrypt ===> "); result_Decrypt.dump();

// 输出
printf("szResult ===> %s\r\n", (char*)(result_Decrypt.value));
printf("szSource ===> %s\r\n", szSource);

return 0;
}


========================================================
部分测试结果:

== mpuint功能测试
unsigned short.sizeof ===> 2
i.sizeof ===> 4
i[0] = 1
i[1] = 3
i.edit ===> 196609
i.edit.printf.0x ===> 30001
i.dump ===> 0001 0003
i.edit.scan.dump ===> 0001 0003

========================================================
哦也,15分钟完成。

Thursday, April 14, 2011

怎么启用google +1 服务?

lp出差,老人把孩子抢过去了,我可以自由了。

我也想试试看google的+1服务。

0,以为很简单,但是在google产品里没有发现入口。

1,又以为放狗去找“google +1”可以轻松搞定。找到了1条官方页面:http://www.google.com/+1/button/里面只有介绍,还是没有启用方法。

2,仔细看了介绍视频还是没说启用方法, 开始有点晕了。猜想可能在google account setting里有,就到里面翻箱倒柜,顺便也整理一下。其间思想开小差,注册了1个http://www.quora.com。之前在ip上看到这时1个关于提问/回答的网站。

3,想起我的google profile上的email is disable。了解+1需要显示推荐人的身份,相当于熟人的推荐才更可信(这也是我很喜欢上开心网的1部分原因)。开启email is on后,还是没有+1 tab。

4,现在的目标不再是开启+1服务了,已经转变为要考考自己怎么独立的解决1个问题了,至于这个问题是什么已经不重要了。总不能1有问题就去问大侠。

5,试试看quora,在上面发表了我的第一个问题:How can the Google +1 service be enabled?
 边上显示类似的问题:How can I get access to Google +1?
里面提示要到http://google.com/experimental,上去。
原来在Google Labs > Experimental search里,也太隐蔽了。是不是不好意思拿出来见人呀?

6,注册后,google主页显示“experiment lab”。搜1个“如何开启google +1服务”看看效果,果然有“+1”了。

7,顺便看到 为你的网站添加Google +1按钮| 爱谷地+1,也为自己的blogger申请+1服务。

8,回到google profile,还是没有+1 tab。是要这么做的:
这是人做的事吗?

9,再看+1tab,的确收藏了我的+1页面。可是毫无可读性,如下所示:
这活儿真的有点粗燥呀,有木有呀?

======================================================================
罗哩罗嗦报了1堆流水账,小结一下解决了3个问题:
1,怎么开启google +1 服务?
2,怎么为自己的网站预先申请+1服务?
3,怎么在google profile里显示+1 tab?

其实从+1介绍页面http://www.google.com/+1/button/的最后1行小字(Learn more about the +1 button and personalization on sites across the web.)出发,去读大段e文,不断跳转,应该也可以解决3个问题的。

其他感想:
1,自己解决问题的效率偏低。
也许是被这个官方页面给吓住了。后面的方法显得没有了条理。
针对具体问题,没有找到合适的答案,应该马上改用其他关键字。

2,quora的效率还是挺高的,不但可以马上找到类似问题。
我自己的提问的回复也很快到了。

Friday, March 11, 2011

有这么2个纳粹

谁说iptv没货?前段时间在上面看了这么2部片子:
伪钞制造者:
无耻混蛋

2个片子从不同的角度讲述了2战,前者是真实的故事,后者有点荒诞。都值得去看。

因为没有及时去写影评,所以留下不少时间去比较、综合这2部片子。从中发现有这么2个纳粹很有些相似,而且在其他地方都没有见过。

伪钞制造者中,集中营的长官以前是个有理想的共产党。“为了生活”投身纳粹,过上了老婆孩子热炕头的好日子。纳粹要倒台了,他利用工作之便带上集中营自制的美元,准备逃到美国去。最后好像是被干掉了。

无耻混蛋中,那个中校,号称"犹太猎人", 心狠手辣,扑杀犹太人他最卖力。从好的方面看,可以说是业务能力超强。形势不好了,他希望以放生刺杀小组为筹码移民美国。结果虽然捡了一条命,在道义上还是受到了应有的惩罚,额头上被刻了1个纳粹徽章。

这2个纳粹都是人精,能力超强,为了获得最大的利益,怎么做都行。可惜人算不如天算,最终还是栽了。

落实到现实生活,做人要厚道,不能极端自利,这样容易走极端,站错队。我这样的凡胎肉眼,怎么能洞察世间真相?所以还是老实做人,遵循一贯的做人道理,与人为善,总是不错的。

===============================================

下周又要开始新的版本了。但是 i will come back。