yichao firstname, zeaster nickname, zhang lastname

Inconvenienced when using Contact Content Provider on Android

These days, I've been developing an application that reads and writes entries from/into Android Contacts.
Everything goes fine until I need to insert email entry into Android Contact by Contact Content Provider API.

This is how to insert a people or phone entry into contact (people, phones table):
getContentProvider().insert(People.CONTENT_URI, values);
getContentProvider().insert(Phones.CONTENT_URI, values);

That's fine according to
http://code.google.com/android/devel/data/contentproviders.html

However when I insert an email entry into contact (in contact_methods table) using:
ContentURI uri = getContentProvider().insert(ContactMethods.CONTENT_URI, values);

It just returns null, nothing inserted, and nothing shows up on logcat.
Why? the link above says it should be fine!
To find the answer, I post a thread in Google Group:
http://groups.google.com/group/android-developers/browse_thread/thread/9ed4ff7fc1338b7c
However no response.
then I have been diving into build-in Contacts Application for 2 days to find how Google used their own api.
Luckily, I got the key.
see details on my another post:
http://zeaster.blogspot.com/2007/11/how-to-decompile-dex-file-on-android_28.html

You should insert an email like this:
ContentURI uri = People.CONTENT_URI.addId(user_id).addPath("contact_methods");
ContentValues values = ...
uri = getContentResolver().insert(uri, values);

why? I guess Google should provide one same way to insert content.

That's the main inconvenience.
I guess what's even worse is Google does not provide enough docs or log warnings for this tip.
Here are the others:

2.
when insert an email for a not-created-yet user id, It's still inserted successfully.
when the user id created in the future, the user would have the email address.
I guess Google should prevent this happen.
Actually many checks Google need to do.That's really a beta SDK.

3.
The content provider API is not Object-Oriented enough.
I feel it tedious to use.

4.
Maybe this is greedy.
I hope there is API to manipulate database that's similar to ActiveRecord for Rails.
Maybe I will implement one like that if time permits.

How to decompile .dex file on Android

Lucky to see the Google Android build-in Contacts is also developed by Android SDK.
This article will take it for example and discuss these How-To's:

1 How to find the Contacts App file on Google Android
2 How to find the Contacts App's classes.dex on Google Android
3 How to dump the Contacts App's classes
4 How to decompile the dumped file

{
Actually, I think the Contacts Content Provider that Google provides has some inconvenience to use.
And I will post another blog to list what's inconvenience and request some improvement from Google.
The blog link is here:
http://zeaster.blogspot.com/2007/11/inconvenienced-when-using-contact.html
}

1 How to find the Contacts App on Google Android
Using adb tool

$ adb shell
# cd /system/app
cd /system/app
# ls
ls
-rw-r--r-- root root 25519 2007-11-14 20:40 ContactsProvider.apk
-rw-r--r-- root root 7544 2007-11-14 20:40 GoogleAppsProvider.apk
-rw-r--r-- root root 16198 2007-11-14 20:40 ImProvider.apk
-rw-r--r-- root root 20308 2007-11-14 20:40 MediaProvider.apk
-rw-r--r-- root root 21272 2007-11-14 20:40 TelephonyProvider.apk
-rw-r--r-- root root 11809 2007-11-14 20:40 SettingsProvider.apk
-rw-r--r-- root root 418688 2007-11-14 20:41 Browser.apk
-rw-r--r-- root root 68077 2007-11-14 20:41 Contacts.apk
-rw-r--r-- root root 96287 2007-11-14 20:41 Development.apk
-rw-r--r-- root root 44790 2007-11-14 20:41 GoogleApps.apk
-rw-r--r-- root root 8637 2007-11-14 20:41 Fallback.apk
-rw-r--r-- root root 99431 2007-11-14 20:41 Home.apk
-rw-r--r-- root root 171614 2007-11-14 20:41 Maps.apk
-rw-r--r-- root root 424601 2007-11-14 20:41 Phone.apk
-rw-r--r-- root root 192119 2007-11-14 20:41 XmppService.apk
-rw-r--r-- root root 6614 2007-11-14 20:41 XmppSettings.apk
#

The Contacts.apk is the Contacts App and ContactsProvider.apk is the app that provides the Contact Content Provider for reading and writing "content://contacts/" resources.
we can see many system apps developed by Android Java SDK are put here.
However what is inside *.apk file. Actually it is a .zip file. It is easy to unzip and get the answer.
take Contacts.apk for example, it contains:

META-INF\
res\
AndroidManifest.xml
classes.dex
resources.arsc

The classes.dex file contains all compiled Java code.

2 How to find the Contacts App's classes.dex on Google Android
No need to unzip Contacts App's classes.dex file from Contacts.apk file.
The Dalvik VM has a cached file for us. Go head to find it.

$ adb shell
# cd /data/dalvik-cache
cd /data/dalvik-cache
# ls
ls
-rw-rw-rw- root root 73852 2007-11-27 05:16 system@app@Contacts.apk@classes.dex
-rw-rw-rw- app_0 app_0 64172 2007-11-27 05:17 system@app@ContactsProvider.apk@classes.dex
-rw-rw-rw- root root 15204 2007-11-27 05:17 system@framework@am.jar@classes.dex
-rw-rw-rw- app_3 app_3 3012 2007-11-27 07:33 system@app@Fallback.apk@classes.dex
-rw-rw-rw- root root 7252804 2007-11-27 05:16 system@framework@core.jar@classes.dex
....and many other cached files.

3 How to dump the Contacts App's classes
Unfortunately the classes.dex is not a .jar file. It's Dalvik executable format that is optimized for efficient.
so we can not pull the .class files as .jar format. However Google Android provides a tool named dexdump included in the emulator to dump .dex file. Here is how to use it.
# dexdump
dexdump: no file specified
dexdump: [-f] [-h] dexfile...

-d : disassemble code sections
-f : display summary information from file header
-h : display file header details
-C : decode (demangle) low-level symbol names
-S : compute sizes only

Now we dump the system@app@Contacts.apk@classes.dex and system@app@ContactsProvider.apk@classes.dex.

# dexdump -d -f -h -C system@app@Contacts.apk@classes.dex >> Contacts.apk.dump
# dexdump -d -f -h -C system@app@ContactsProvider.apk@classes.dex >> ContactsProvider.apk.dump

and then pull it out.
# exit
$ adb pull /data/dalvik-cache/Contacts.apk.dump ~/android
$ adb pull /data/dalvik-cache/ContactsProvider.apk.dump ~/android

Now we get the dumped files on ~/android folder.

4 How to decompile the dumped file
.dex file is optimized, so we can not decompile it as normal .class file.
Fortunately the dumped file is a bit readable for me.
As it is too big to analyze all the code, so I just take "how to create a contact" for example.
It's easy to get this info from the dumped file.
It has these classes:
...
Class name : 'com/google/android/contacts/AttachImage'
Class name : 'com/google/android/contacts/ContactEntryAdapter'
Class name : 'com/google/android/contacts/EditContactActivity'
Class name : 'com/google/android/contacts/EditContactActivity$EditEntry'
...

The EditContactActivity class has a private void create() method.
#3 : (in com/google/android/contacts/EditContactActivity)
name : 'create'
type : '()V'
access : 0x0002 (PRIVATE)

So all the code that create a contact is here from the dumped file, my comments inlined:
// private void create()
009b24: |[009b24] com/google/android/contacts/EditContactActivity.create:()V

// ContentValues v9 = new ContentValues();
009b28: 2109 3100 |0000: new-instance v9, android/content/ContentValues // class@0031
009b2c: 6f01 0f00 0900 |0002: invoke-direct {v9}, android/content/ContentValues.:()V // method@000f

// Entry v12 = this.getCurrentEntry();
009b32: f4fc a800 |0005: +iget-object-quick v12, v15, [obj+00a8]

// int v11 = v12.lines
009b36: f801 1500 0c00 |0007: +invoke-virtual-quick {v12}, [0015] // vtable #0015
009b3c: 0a0b |000a: move-result v11

// int v5 = 0
009b3e: 1205 |000b: const/4 v5, #int 0 // #0

// if v5 >= v11 goto 0046
009b40: 3ab5 3a00 |000c: if-ge v5, v11, 0046 // +003a

// EditEntry v8 = (EditEntry)this.getCurrentEntry();
009b44: f4fc a800 |000e: +iget-object-quick v12, v15, [obj+00a8]
009b48: f802 1a00 5c00 |0010: +invoke-virtual-quick {v12, v5}, [001a] // vtable #001a
009b4e: 0c0c |0013: move-result-object v12
009b50: 07c0 |0014: move-object v0, v12
009b52: 1e00 3d00 |0015: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009b56: 0708 |0017: move-object v8, v0

// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009b58: f28c 1400 |0018: +iget-quick v12, v8, [obj+0014]

// int v13 = -3 // PHOTO_KIND = -3 defined in v8's superclass Entry
009b5c: 12dd |001a: const/4 v13, #int -3 // #fd

// if v12 != v13 goto 0020 // a photo entry, goto 0020
009b5e: 38dc 0500 |001b: if-ne v12, v13, 0020 // +0005

// int v5++;
009b62: d805 0501 |001d: add-int/lit8 v5, v5, #int 1 // #01

// goto 000c
009b66: 33ed |001f: goto 000c // -0013

// String v7 = v8.getData();
009b68: f801 0c00 0800 |0020: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009b6e: 0c07 |0023: move-result-object v7

// boolean v12 = TextUtils.isEmpty(v7)
009b70: 7001 4200 0700 |0024: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009b76: 0a0c |0027: move-result v12

if v12!=false/0 goto 0030
009b78: 3e0c 0800 |0028: if-nez v12, 0030 // +0008

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b7c: f48c 2800 |002a: +iget-object-quick v12, v8, [obj+0028]

// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009b80: f803 0b00 c907 |002c: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b

// goto 001d
009b86: 33ee |002f: goto 001d // -0012

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b88: f48c 2800 |0030: +iget-object-quick v12, v8, [obj+0028]

// String v13 = "name";
009b8c: 180d 1c00 |0032: const-string v13, "name" // string@001c

// boolean v12 = v12.equals(v13);
009b90: ee02 0300 dc00 |0034: +execute-inline {v12, v13}, java/lang/String.equals:(Ljava/lang/Object;)Z // inline #0003
009b96: 0a0c |0037: move-result v12

// if v12 != false/0, goto 001d
009b98: 3d0c e5ff |0038: if-eqz v12, 001d // -001b

// String v12 = "EditContactActivity"
009b9c: 180c 0100 |003a: const-string v12, "EditContactActivity" // string@0001

// String v13 = "Name is required"
009ba0: 180d 0200 |003c: const-string v13, "Name is required" // string@0002

// Log.e(v12, v13); //log info using android/util/Log
009ba4: 7002 4700 dc00 |003e: invoke-static {v12, v13}, android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I // method@0047
009baa: 0a0c |0041: move-result v12

// call this.XXX()
009bac: f801 8400 0f00 |0042: +invoke-virtual-quick {v15}, [0084] // vtable #0084

// return void
009bb2: 0e00 |0045: return-void

// ContentResolver v12 = this.getContentResolver();
009bb4: f4fc a400 |0046: +iget-object-quick v12, v15, [obj+00a4]

// CONTENT_URI v13 = People.CONTENT_URI;
009bb8: 610d 0300 |0048: sget-object v13, android/provider/Contacts$People.CONTENT_URI:Landroid/net/ContentURI; // field@0003

// CONTENT_URI v10 = v12.insert(v13, v9) // CONTENT_URI v10 = getContentResolver().insert(uri, values);
// insert people into contacts
009bbc: f803 1100 dc09 |004a: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009bc2: 0c0a |004d: move-result-object v10

// List v12 = this.mContactEntries
009bc4: f4fc c000 |004e: +iget-object-quick v12, v15, [obj+00c0]
009bc8: 7001 8900 0c00 |0050: invoke-static {v12}, com/google/android/contacts/ContactEntryAdapter.countEntries:(Ljava/util/ArrayList;)I // method@0089
009bce: 0a06 |0053: move-result v6

// int v5 = v11 //contact Entry Count
009bd0: 01b5 |0054: move v5, v11

// if v5 >= v6, goto 009e
009bd2: 3a65 4900 |0055: if-ge v5, v6, 009e // +0049

// List v12 = this.mContactEntries
009bd6: f4fc c000 |0057: +iget-object-quick v12, v15, [obj+00c0]

// EditEntry v8 = (EditEntry) getEntry(v12, v5);
009bda: 7002 8b00 5c00 |0059: invoke-static {v12, v5}, com/google/android/contacts/ContactEntryAdapter.getEntry:(Ljava/util/ArrayList;I)Lcom/google/android/contacts/ContactEntryAdapter$Entry; // method@008b
009be0: 0c0c |005c: move-result-object v12
009be2: 07c0 |005d: move-object v0, v12
009be4: 1e00 3d00 |005e: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009be8: 0708 |0060: move-object v8, v0

// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009bea: f28c 1400 |0061: +iget-quick v12, v8, [obj+0014]

// int v13 = -1 // CONTACT_KIND = -1 defined in v8's superclass Entry
009bee: 12fd |0063: const/4 v13, #int -1 // #ff

// if v12 == v13 goto 007e
009bf0: 37dc 1a00 |0064: if-eq v12, v13, 007e // +001a

// v9.clear() // ContentValues.clear()
009bf4: f801 1500 0900 |0066: +invoke-virtual-quick {v9}, [0015] // vtable #0015

// boolean v12 = v8.toValues(v9) // boolean EditEnty.toValues(ContentValues cv)
009bfa: f802 0d00 9800 |0069: +invoke-virtual-quick {v8, v9}, [000d] // vtable #000d
009c00: 0a0c |006c: move-result v12

//if v12 !=false/0, goto 007b
009c02: 3d0c 0e00 |006d: if-eqz v12, 007b // +000e

// ContentResolver v12 = this.getContentResolver();
009c06: f4fc a400 |006f: +iget-object-quick v12, v15, [obj+00a4]

// String v13 = v8.contentDirectory
009c0a: f48d 2c00 |0071: +iget-object-quick v13, v8, [obj+002c]

// v10.addPath(v13);
009c0e: f802 1800 da00 |0073: +invoke-virtual-quick {v10, v13}, [0018] // vtable #0018
009c14: 0c0d |0076: move-result-object v13

// CONTENT_URI v12 = v12.insert(v13, v9) // CONTENT_URI v12 = getContentResolver().insert(uri, values);
009c16: f803 1100 dc09 |0077: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009c1c: 0c0c |007a: move-result-object v12

// int v5++;
009c1e: d805 0501 |007b: add-int/lit8 v5, v5, #int 1 // #01

// goto 0055
009c22: 33d8 |007d: goto 0055 // -0028

// String v7 = v8.getData();
009c24: f801 0c00 0800 |007e: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009c2a: 0c07 |0081: move-result-object v7

// v9.lines
009c2c: f801 1500 0900 |0082: +invoke-virtual-quick {v9}, [0015] // vtable #0015

// boolean v12 = TextUtils.isEmpty(v7)
009c32: 7001 4200 0700 |0085: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009c38: 0a0c |0088: move-result v12

if v12!=false/0 goto 007b
009c3a: 3e0c f2ff |0089: if-nez v12, 007b // -000e

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009c3e: f48c 2800 |008b: +iget-object-quick v12, v8, [obj+0028]

// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009c42: f803 0b00 c907 |008d: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b

// ContentResolver v12 = this.getContentResolver();
009c48: f4fc a400 |0090: +iget-object-quick v12, v15, [obj+00a4]

// int v13 = 0
009c4c: 120d |0092: const/4 v13, #int 0 // #0

// int v14 = 0
009c4e: 120e |0093: const/4 v14, #int 0 // #0

009c50: 07c0 |0094: move-object v0, v12
009c52: 07a1 |0095: move-object v1, v10
009c54: 0792 |0096: move-object v2, v9
009c56: 07d3 |0097: move-object v3, v13
009c58: 07e4 |0098: move-object v4, v14

// int v12 = v12.update(v1,v2,v3,v4) // getContentResolver(),update(uri, values, null, null)
// refer to this method, public final int update(ContentURI uri, ContentValues values, String where, String[] selectionArgs)
009c5a: f905 1400 0000 |0099: +invoke-virtual-quick/range {v0, v1, v2, v3, v4}, [0014] // vtable #0014
009c60: 0a0c |009c: move-result v12

// goto 007b
009c62: 33de |009d: goto 007b // -0022

// int v12 = 4
009c64: 124c |009e: const/4 v12, #int 4 // #4

009c66: f5fc c800 |009f: +iput-quick v12, v15, [obj+00c8]
009c6a: f7fa 9000 |00a1: +iput-object-quick v10, v15, [obj+0090]

// int v12 = -1
009c6e: 12fc |00a3: const/4 v12, #int -1 // #ff
009c70: f801 0700 0a00 |00a4: +invoke-virtual-quick {v10}, [0007] // vtable #0007
009c76: 0c0d |00a7: move-result-object v13
009c78: f803 7f00 cf0d |00a8: +invoke-virtual-quick {v15, v12, v13}, [007f] // vtable #007f

// goto 0045
009c7e: 339a |00ab: goto 0045 // -0066
exceptions : (none)
positions : 30
0x0000 line=471
.....
0x00a3 line=515
locals : 10
//the 10 local variables defined here
0x0000 - 0x00ac reg=15 this Lcom/google/android/contacts/EditContactActivity;
0x0002 - 0x00ac reg=9 values Landroid/content/ContentValues;
0x000b - 0x00ac reg=11 contactEntryCount I
0x000c - 0x00ac reg=5 i I
0x0018 - 0x0045 reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0x0024 - 0x0045 reg=7 data Ljava/lang/String;
0x004e - 0x00ac reg=10 contactUri Landroid/net/ContentURI;
0x0054 - 0x00ac reg=6 entryCount I
0x0061 - 0x009e reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0x0082 - 0x009e reg=7 data Ljava/lang/String;

blogsync FAQ

 



blogsync FAQ


from http://code.google.com/p/blogsync-java/wiki/BlogsyncFAQ

what is blogsync?


it is used to import wordpress posts into blogger.

how to use it?



  • set jdk bin path in run.bat/run.sh

  • execute run.bat/run.sh


what is new in blogsync 0.3?


The comments can be imported from wordpress rss xml file to blogger site now. However it can not preserve the published time of the wordpress comments. so the published time of your imported comments is just the time when you import.

what is new in blogsync 0.2?


add feature to import posts from exported wordpress rss xml file to blogger site.

which jdk version do I need to run blogsync?


You should use jdk1.5 or later.

how to add jdk bin path in path environment?



  • windows

    1. says your java.exe is in directory c:\jdk1.5.0\bin

    2. set the following line in run.bat

    3.     set path=c:jdk1.5.0bin;%path%



  • Mac or unix

    1. says your java is in directory /path/to/your/java

    2. set the following line in run.sh

    3.     export PATH=/path/to/your/java:$PATH




how do I know which version of jdk I am using?


open a terminal, type "java -version". you should get something like this:
java version "1.5.0_12"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_12-b04)
Java HotSpot(TM) Client VM (build 1.5.0_12-b04, mixed mode)

how to run run.bat/run.sh in command line?



  • windows

  • open a command window, type:
        cd e:pathtoyourrun.bat
    run
    .bat


  • Mac or unix

  • open a terminal, type:
        cd /path/to/your/run.sh
    sh run
    .sh



when run run.sh, it is opened by my editor, what should I do?


please run run.sh in command line.

after clicking on run.bat/run.sh, there was nothing happend, what should I do?


run run.bat/run.sh in command line so that find what's going wrong in the terminal?

It prompts "java: command not found", what's going wrong?


java is not in your path, please add jdk bin path in your path environment.

why do I get Exception in thread "main" java.lang.NoClassDefFoundError: org/easter/blogsync/BlogSync?


if you are on Mac or unix, please make sure you have read permission on blogsync/build/blogsync.jar file if not, open a terminal and type:
chmod 744 /path/to/your/blogsync/build/blogsync.jar

why do I get Failed to create input stream: Server returned HTTP response code: 403 for URL: http://mydomain.com/wordpress/xmlrpc.php?



  • if you installed a plugin called Enforce www. Preference, it would affect the path to your xmlrpc.php file, so deactivated it first.

  • if you enabled mod_security on apache, it would block access to xmlrpc.php, so add this to your .htaccess file:

  •     <Files xmlrpc.php>
    SecFilterInheritance Off
    </Files>


python script to email me the dynamic ip from tp-link router

At home, my boxes are connected to internet by Beijing ADSL.
when connected, Beijing ADSL gives me a dynamic ip and it changes about every 24 hours.
Before I get the dynamic ip by a service provided by oray.net.
However recently the service is broken.
So I have to write a python script to check the dynamic ip from my tp-link router every 3 minutes and if it changed, emails the new dynamic ip to my gmail.

I also write a bash script to run the above python script at boot on my gentoo linux.

The python script:


#!/usr/bin/python

import urllib
import urllib2
import libgmail
import cookielib
import sys
import re
import base64
import socket
import time
from urlparse import urlparse

nowip=''
logfile=open('/var/log/ipsender.log','w')

def log(s):
now=time.strftime('%Y-%m-%d %X')
logfile.write('%s %sn'%(now,s))
logfile.flush()

def gmailsender(to_addr,subject,msg):
username='xxx@gmail.com'
password='xxx'
ga=libgmail.GmailAccount(username,password)
ga.login()
gmsg=libgmail.GmailComposedMessage(to_addr,subject,msg)
ga.sendMessage(gmsg)

def check():
global nowip
timeout=10
socket.setdefaulttimeout(timeout)
username = 'admin'
password = 'xxx'
try:
theurl='http://192.168.1.1/userRpm/StatusRpm.htm'
req = urllib2.Request(theurl)
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
authheader = "Basic %s" % base64string
req.add_header("Authorization", authheader)
handle = urllib2.urlopen(req)
str=handle.read()
ss='var wanPara = new Array'
start=str.index(ss)+ss.__len__()
end=str.index(';',start)
s2=str[start:end]
log(s2)
t=eval(s2)
log(t[2])
handle.close()
if nowip!=t[2]:
nowip=t[2]
gmailsender('mygmail@gmail.com','ddnsip===%s'%nowip,s2)
log('send %s successfully.'%nowip)
else:
log('ddnsip remains %s.'%nowip)
except:
log('Unexpected error:', sys.exc_info()[0])

def main():
while True:
check()
time.sleep(3*60)

main()

The bash script put in /etc/init.d/:

#!/sbin/runscript
# Copyright 1999-2004 Gentoo Foundation
start() {
ebegin "Starting ipsender"
nohup python /home/zeaster/amp/ipsender.py >> /var/log/ipsender-nohup.log&
eend $?
}
stop() {
ebegin "Stopping ipsender unsuccessfully"
eend $?
}

wake-on-lan by python script

How to wake up your computer on the LAN?
the answer is just to send a Magic Packet to your computer on port 7 or 9.
However when your computer is down, it has no ip, so send it to the broadcast ip
the following python script implements the above idea.


import struct, socket

def wake_on_lan(ether_addr, inet_addr):
addr_byte=ether_addr.split(':')
hw_addr=struct.pack('BBBBBB',
int(addr_byte[0],16),
int(addr_byte[1],16),
int(addr_byte[2],16),
int(addr_byte[3],16),
int(addr_byte[4],16),
int(addr_byte[5],16))
msg='\xff'*6+hw_addr*16

s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(msg, (inet_addr,7))
s.close()

wake_on_lan('00:E0:4C:E1:D9:CC', '192.168.1.255')


The code above is based on socket.SOCK_DGRAM to send UDP packet.
How to create a UDP packet based on raw socket in python?
the project dpkt does an excellent job.
the following example shows that:

import socket
from dpkt import ethernet,udp,ip
import dpkt
import struct
import string

iface = "eth1"
mac = "00:0E:35:AB:9B:40"
inet = "192.168.1.6"
debug = 0

def eth_aton(buffer):
addr =''
temp = string.split(buffer,':')
buffer = string.join(temp,'')
for i in range(0, len(buffer), 2):
addr = ''.join([addr,struct.pack('B', int(buffer[i: i + 2], 16))],)
return addr

def buildUdp():
ether_addr='00:E0:4C:E1:D9:CD'
addr_byte=ether_addr.split(':')
hw_addr=struct.pack('BBBBBB',
int(addr_byte[0],16),
int(addr_byte[1],16),
int(addr_byte[2],16),
int(addr_byte[3],16),
int(addr_byte[4],16),
int(addr_byte[5],16))
msg='xff'*6+hw_addr*16

udp_p = udp.UDP()
udp_p.sport = 0x8130
udp_p.dport = 7
udp_p.data = msg
udp_p.ulen += len(udp_p.data)

ip_p = ip.IP()
ip_p.src = socket.inet_aton('192.168.1.6');
ip_p.dst = socket.inet_aton('192.168.1.255');
ip_p.off = 0x4000
ip_p.data = udp_p
ip_p.p = ip.IP_PROTO_UDP
ip_p.len += len(udp_p)

packet = ethernet.Ethernet()
packet.src = eth_aton(mac)
packet.dst = eth_aton('ff:ff:ff:ff:ff:ff')
packet.data = ip_p
packet.type = ethernet.ETH_TYPE_IP

if debug: print dpkt.hexdump(str(packet))
return packet

s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((iface,ethernet.ETH_TYPE_IP))
packet = buildUdp()
s.send(str(packet))

blogsync 0.3 released, comments can be imported.

Firstly, the foregoing blogsync 2.0 should be blogsync 0.2. :-)
blogsync is a tool that import your wordpress to blogger from rss xml file or online

Now it's blogsync 0.3 release. and it can import comments from exported wordpress rss xml file to blogger site.
it can be downloaded from:
http://code.google.com/p/blogsync-java/downloads/list

however it can not preserve the published time of the original comments.
so the published time of your imported comment is just the time when you import.
more details can be found at
http://groups.google.com/group/bloggerDev/browse_thread/thread/15c9a45416305f99

blogsync 2.0 released, import your wordpress to blogger from rss xml file or online

It's open source now and comes with new feature.

1 project hosted:
at http://code.google.com/p/blogsync-java
blogsync 0.2 can be downloaded at
http://blogsync-java.googlecode.com/files/blogsync-0.2.zip

2 new feature:
In previous blogsync, it imports wordpress posts to blogger from your wordpress website online.
Now in blogsync 0.2, it can import wordpress posts to blogger from a rss xml file that exported by wordpress.

when blogsync 0.1, the common errors are such as:
a)[Fatal Error] :1444:21: Invalid byte 1 of 1-byte UTF-8 sequence.
org.apache.xmlrpc.client.XmlRpcClientException: Failed to parse
servers response: Invalid byte 1 of 1-byte UTF-8 sequence.
b)Fatal Error] :1:1: Content is not allowed in prolog.

this is because
a) the server response is not in utf-8 encoding. or
b) the server response is not a valid xml.

when your posts are too big, for example, http://i978.iyublog.com, when export all of this blog's posts, the exported rss xml file is 2064KB.
so when transferring these data through http online, the above errors are easy to reproduce.

Few days ago, I tried to export all posts to a rss xml file, then imports these posts locally from the exported xml file, the above error is hard to reproduce again.
the worst case is that your export file is still invalid xml file or wrong encoding, however it's easy to find these errors and you can try to export it again or edit it manually until you get a valid utf-8 xml file. Now you can import these posts to blogger from the valid utf-8 rss xml file by blogsync 2.0.

ps:
how to export your wordpress posts to a rss xml file?
in wordpress admin page, go to "Manage" page -> "Export" page, there is a link to export all of your posts.

ps2:
within 24 hours, Google allows us to submit at most 50 posts by googe data api.
so after 50 posts, the tool can not import any. only to wait for another 24 hours.

统一Mac OS X,Windows XP/Vista以及linux的系统时间

Windows把BIOS时间作为系统时间,而Mac OS X把BIOS时间作为GMT+0时间,所以对于生活在GMT+8时区的中国用户来说,这两个系统共存时系统时间是不一致的。
即:如果windows时间为12:00,则到Mac OS X下就变成了20:00,在Mac OS X下改过来后,再回到windows下就又错了。

之前看过这篇blog,讲了通过修改windows注册表,使得windows也把bios时间作为GMT+0时间,这样就可以解决这个问题了,具体操作如下:
在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\中加一项DWORD,RealTimeIsUniversal,把值设为1即可。

这个办法是靠调整windows时间设置来解决问题的,但是当系统中有linux时,因为linux也是使用bios时间作为系统时间,所以这时linux系统的时间就又不一致了。
最好的办法就是调整Mac OS X的时间。于是想到了强大的Bash Shell,先写脚本1用来开机时自动把时间校对准,然后关机时再运行脚本2,把时间调整回去。
脚本1:
#!/bin/bash
d=$(date "+%d")
H=$(date "+%H")
M=$(date "+%M")
S=$(date "+%S")
let "H=$H-16"

z=0
if [ "$H" -lt "$z" ]
then
  let "H=$H+24"
  let "d=$d-1"
fi

z=10

if [ "$d" -lt "$z" ]
then
  d=0$d
fi

if [ "$H" -lt "$z" ]
then
  H=0$H
fi

sudo date -u $d$H$M.$S

脚本2
#!/bin/bash
ss=$(date "+%d%H%M.%S")
sudo date -u $ss

那么如何实现自动开机运行以及关机自动运行呢?
1 开机自动运行
写shell,然后在applescript中调用shell,再把applescript保存为app,添加到login items中
注:我的.sh文件和textmate做绑定了,这样直接添加.sh文件不会执行shell,而是被textmate打开

2 关机自动运行
使用launchbar的restart和shutdown applescript,因此只需在这两个script中添加调用shell的一句即可了。

bash shell修改系统时间需要root权限,那么如何在applescript中调用时输入root密码呢?
使用如下命令:
do shell script "sudo date -u 1200" password "mypwd" with administrator privileges

推荐一本书:中国自行车之旅

前面介绍了如何选购适合自己的自行车,以及一些基本修车技术,足以应付长途骑行的需要。
后面是全国各地的骑行路线介绍,很实用!

PS 1:
本来想引用豆瓣上关于这本书的链接,结果发现没有,仔细一查才发现原来:
在豆瓣上已无法添加《中国自行车之旅》一书,因为《中国自行车之旅》和《中国徒步之旅》这两本书的isdn号都是7561327234
  这两部书见:
  http://joyo.com/detail/product.asp?prodid=bkbk404144
  http://joyo.com/detail/product.asp?prodid=bkbk410714

PS 2:
看过之后,决定今年春季去一次十三陵水库,那位想一同去,可以留言。

性能准则之一:在遍历集合前一定要判断集合是否为空

例举2个例子:
1使用Iterator对象遍历集合时,此时遍历前检查集合大小可以缩短执行时间约为:64%

  public static void main(String[] args) {
    int N = 1000 * 100000;
    List<Integer> list = new ArrayList<Integer>();

    long start = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
      Iterator<Integer> iter = list.iterator();
      while (iter.hasNext()) {
        System.out.println(iter.next());
      }
    }

    System.out.println(System.currentTimeMillis() - start);                      //==================490
    start = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
      int j = list.size();
      if (j > 0) {
        Iterator<Integer> iter = list.iterator();
        while (iter.hasNext()) {
          System.out.println(iter.next());
        }
      }
    }
    System.out.println(System.currentTimeMillis() - start);                     //==================180          时间缩短了64%
  }


2 使用for循环遍历集合时,此时遍历前检查集合大小可以缩短执行时间约为:5%
  public static void main(String[] args) {
    int N = 1000 * 10000;
    List<Integer> list = new ArrayList<Integer>();

    long start = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
      int j = list.size();
      for (int t = 0; t < j; t++) {
        System.out.println(list.get(t));
      }
    }

    System.out.println(System.currentTimeMillis() - start);          //==================190
    start = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
      int j = list.size();
      if (j > 0) {
        for (int t = 0; t < j; t++) {
          System.out.println(list.get(t));
        }
      }
    }
    System.out.println(System.currentTimeMillis() - start);         //==================180     时间缩短了5%
  }

blogsync with gui coming, import your wordpress to blogger

blogsync with gui coming

and with some control function about which posts to import.

it can be downloaded from
http://yichao.zhang.googlepages.com/blogsync-with-gui.tar.gz

Readme.txt

0. What is it used for?
This is a simple tool to import all posts from wordpress to blogger/blogspot.

1. How to use it?
0) set java in your path.
for windows users, run run.bat
for unix/mac users, run run.sh
1) set up your wordpress and blogger account.
2) choose one import option from option panel.
3) read posts from wordpress.
4) check posts and remove any posts that you do not want to import by selecting and right-clicking.
5) import now by just clicking the import button.

2. what is my blogger blogid?
It's in your blogspot Dashboard url, for example: http://www2.blogger.com/posts.g?blogID=18083698
"18083698" is your blogid.

3. what may cause this error?
[Fatal Error] :1444:21: Invalid byte 1 of 1-byte UTF-8 sequence.
org.apache.xmlrpc.client.XmlRpcClientException: Failed to parse
servers response: Invalid byte 1 of 1-byte UTF-8 sequence.

One of your posts may be an invalid xml.
please correct it and make sure its source code is valid xml, then import again.

4. any other questions?
Welcome to send your feedback to Yichao.Zhang & gmail.com
or leave me a comment on http://zeaster.blogspot.com

春运火车票攻略

1
春运背景介绍:
a)票贩子绝对和铁路部门没关
b)铁路部门绝对不想趁春运期间抬高票价
c)政府绝对在春运问题上竭尽全力为人民服务了
d)中国老百姓在春运问题上绝对有觉悟维护自己的权益
此处省去1万字

2
时间:年二十八晚6点
地点:北京火车站退票大厅
人物:票贩男甲
对话:哈尔滨的啦,T17,今晚9:05发车,有座了。。。。/ 多少钱? / 400元 / 。。。。

时间:年二十八晚8点15分
地点:北京火车站退票大厅
人物:票贩女
对话:哈尔滨的啦。。。。/ 多少钱? / 154元 / 。。。。

3 返程
本人脑子有问题,错失购买学生票良机,只好买了站票
带了2个凳子,花了20元,让小红帽带着提前上车了。对了,上车前竟然遇到高中好友,匆忙中也没聊上几句
20元还算有所值,提前占了一个2节车厢中间的位置,摆好箱子当座子,支起椅子坐上,倒也逍遥,此处温度适宜,如果没有后来的事儿,我觉得比有座好
接下来说说后来的事儿:
沈阳站停车,乘务员要从我这个位置开门倒垃圾下去,结果垃圾袋破了,洒出n多方便面汁儿,弄得到处都是,女友牺牲了一个垫椅子的围巾。
乘务员一句道歉的话也没有,惹得女友痛痛快快的骂了一车。

老爸的酒瘾

1
新年回家,老妈拿出2大瓶自制"葡萄酒",就是用葡萄和枸杞子加上白酒泡制而成。
里面泡的葡萄也没扎个眼儿,一问老妈说这是老爸要求的。
别人不知道,我可清楚,这是老爸的变相喝酒法。

2
前几年回家,听说老爸拿蝎子泡酒,说是有营养。
嗯,这是另一种。。。

Spoofing the User Agent with Safari for cmwap

In Mac OS X terminal:
chao:~ mac$ defaults write com.apple.Safari CustomUserAgent "\"hicmwapiamcomingnow\""

Now your safari user-agent has been changed to "hicmwapiamcomingnow".
OK, go internet surfing with cmwap on Mac OS X!

P.S:
of course, you can go with gprsproxy tool without changing anything for safari.
see this for details:
http://zeaster.blogspot.com/2007/02/mac-os-xdopod-696-wm50-cmwap.html

cmwap is a wap service provided by China Mobile.

Windows XP/Vista vs Mac OS X 软件对比清单

自从切换到Mac OS X平台下,就被吸引了,也不想回windows了。
不过刚开始发现windows上还是有几个软件是Mac OS X下没有的,不过现在这种软件越来越少了
现列举几个和我密切相关的说明一下:

1 Microsoft Office for Mac 通过!pass
这个是没用过Mac的人不能想象的,不过它真的有,微软自己给Apple Mac OS X 做的Office 套件
现在版本是Microsoft Office for Mac 2004,和Microsoft Office 2003功能一样
另外,Microsoft已经公布Microsoft Office for Mac 2008也将马上发布。这版的功能和Microsoft Office 2007是一样的
详见:http://www.microsoft.com/mac/products/office2004/

2 ipmsg 通过!pass
实习时公司内使用的即时聊天工具,一个很小的exe程序。
不过也有Mac OS X下的版本,详见我这篇blog:
http://zeaster.blogspot.com/2007/01/translate-ipmsg-into-gbk-on-mac-os-x.html

3 ActiveSync / Windows Mobile Device Center 通过!pass
我用的手机是dopod 696,Windows Mobile的操作系统,同步工具在XP下是ActiveSync,Vista下是Windows Mobile Device Center
不过也有Mac OS X下的类似软件Missing Sync for Windows Mobile,详见:
http://www.markspace.com/missingsync_windowsmobile.php

4 Dopod 696 USB Modem 无线modem 通过!pass
在Mac OS X 下用Windows Mobile 2003/wm5.0手机连接cmwap或者cmnet上internet?!
没错!这是可以成功!详见我的这篇blog:
http://zeaster.blogspot.com/2007/02/mac-os-xdopod-696-wm50-cmwap.html

5 智能手机工作室 Jeyo 没通过,failed
就是在Mac OS X连接手机发短信,这个功能目前我还没找到可替代的软件

6 招商银行专业版客户端 通过!pass
最近发现原来Windows Mobile下又招商银行客户端,而且可以cmwap连接,本人已试,好用!
成功登陆后使用cmwap进行各项操作均没有问题。不过也有几点需要主要:
1)不要用cmwap恢复专业版证书,会很慢,常常会失败,可以先手机连pc机,恢复完证书,再用cmwap登陆就没问题了!
2)打开招商银行掌上版前,一定要先连同gprs,例如,先打开pie,连个网。
否则招商银行掌上版会找不到网络,它不会自动开启gprs。也许这是为用户安全考虑吧。
另外也督促招商银行尽快出Mac OS X版的客户端。
掌上版详见:
http://www.cmbchina.com/PDAbank/PDADownload.htm


7 蓝牙棒的驱动 没通过,failed
实际我用的Mac OS X是装在IBM Thinkpad上的,没蓝牙驱动

8 三耳可视数字复读机 没通过,failed
我练习听力的软件,可以自动把mp3按句子分割,然后一句一句复读听。没找到Mac版的
详见:http://www.3estudy.com

Mac OS X上成功使用dopod 696 wm5.0 连 cmwap上网

哈哈!
也是使用dopod 696/himalaya wm5.0 的“internet共享”方式上网
1 在Mac OS X 下,安装Missing Sync for Windows Mobile
2 去掉Missing Sync连接方式中的usb,以下和我上篇blog中的说明一样,详见:
http://zeaster.blogspot.com/2007/02/dopod-696-wm50-cmwap.html

蓝牙方式我没试,因为偶是在IBM thinkpad 上装的Mac OS X 10.4.7,没蓝牙驱动,不过道理是一样的,应该也能成功。

笔记本成功使用dopod 696 wm5.0 上的cmwap上网

在各个论坛上查了一圈,知道696升级到wm5.0后原来的USB modem是无法使用
新的方式是使用internet共享。
就是通过USB,蓝牙或红外等方式使得696和笔记本组成一个局域网,在局域网内笔记本可以共享696的 internet 连接
经测试使用USB和bluetooth方式,笔记本都可以连上696的cmwap或cmnet连接。
注明:
我使用的rom是haha2008 0207版 wm5.0 的rom,其他早些的版本需要自己配置696蓝牙网卡的信息,此版rom已设置好。
下载地址:http://www.hi-pda.com/forum/viewthread.php?tid=332225
gprsproxy的下载地址:
http://www.51nb.com/forum/viewthread.php?tid=280524
http://www.51nb.com/forum/viewthread.php?tid=299242

使用usb的方式
1 去掉activesync的usb连接
2 连接696和笔记本
3 此时笔记本上的连接中会新增一个Windows Mobile-based Device连接
4 在696上启动internet连接,pc连接选usb,网络连接选接入点为cmwap的连接(具体名是自己定义的),确定连接
5 这时Windows Mobile-based Device连接会重新启动一下,此时这个连接的网络信息是
        Connection-specific DNS Suffix  . :
        Description . . . . . . . . . . . : Windows Mobile-based Device #3
        Physical Address. . . . . . . . . : 80-00-60-0F-E8-00
        Dhcp Enabled. . . . . . . . . . . : Yes
        Autoconfiguration Enabled . . . . : Yes
        IP Address. . . . . . . . . . . . : 192.168.0.102              ========》共享局域网中笔记本的ip
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.0.1             ========》共享局域网中696的ip
        DHCP Server . . . . . . . . . . . : 192.168.0.1
        DNS Servers . . . . . . . . . . . : 192.168.0.1
5 使用gprsproxy 1.3, 把ie或 firefox 中的代理设置成gprsproxy中配置的,即可上网了。

使用蓝牙的方式
1 笔记本上安装蓝牙驱动
2 开启696和笔记本的蓝牙,并且配对成功,这时笔记本上的连接中会新增一个Bluetooth PAN network Adapter
3 在696上启动internet连接,pc连接选蓝牙,网络连接选接入点为cmwap的连接(具体名是自己定义的),确定连接,696会显示“已完成设备配置,请在pc上连接蓝牙PAN”
4 在笔记本上连接696的蓝牙 PAN 服务即个人局域网服务Personal Area Network。(如果之前已经连接,断开重连一次即可)
5 这时Bluetooth PAN network Adapter会分配到和上面类似的网络配置信息
6 使用gprsproxy 1.3, 把ie或 firefox 中的代理设置成gprsproxy中配置的,即可上网了。

注:
1
cmwap下只要修改user-agent 并且设置10.0.0.172代理就可以上网,不用gprsproxy也能上网
firefox下修改user-agent 的插件:useragentswitcher.xpi
修改代理的插件:switchproxy_tool-1.4.1-fx+mz+tb.xpi

2
在cmwap下上某些网址,如groups.google.com仍会提示xml解析错误,这时可以使用opera9试一下。
在opera9上出现此错误时,旁边会有一个按钮提示让你“按html格式解析”,点击此按钮,就可正常显示html。

3
使用cmnet的情况就更简单了,直接省去上面的gprsproxy配置那一步,不用代理直接可以上网。

dopod 696/himalaya 上再次安装wm5.0手记

第一次安装时没留下点记录,结果这次重新刷rom时走了很多弯路。
1 首先几个概念的介绍:

696的内存 = 128M ram + 64 rom
windows mobile 2003se 下的使用情况:
128M ram = 存储空间 + 内存,并且存储空间和内存所占比例可以动态调整
64M rom = 32M 操作系统 + 16M ExtRom + 16M Storage,ExtRom是隐藏的,平时看不到

windows mobile 2005 下的使用情况:
128M ram = 内存 + RAMdisk + 缓冲池PagingPool,其中这三者的比例分配可以通过修改os rom文件来调整
64M rom = 32M 操作系统 + 32M 存储空间

我使用的是RAMdisk 48M + PagingPool 24M

2 通过这次找rom,发现国内民间的开发活动还不规范
首先对于同一个软件项目,没有一个同一的发布位置,总是分布在各个论坛的各个帖子里
其次对于软件的版本管理很混乱,下载的文件中没有详细的Changs更新说明,实际上这些说明也是分布在各个论坛的各个帖子里
总之信息都是分布于各个论坛的各个帖子,查找起来很困难
国外的类似项目如 xda-developers,osxproject 除了论坛,都会有一个专门的wiki来整理信息,使得信息有一个唯一的入口。
这样利于使用者,也利于其他contributor来奉献
造成这种局面,估计有2个原因:
1 国内的企业中软件开发本来就不规范,所以国内开发者普遍不知道如何规范的开发
2 国内资源太紧张,有些开发者不是不想放到一个同一的地方,而是找不到这样的服务器,没有钱买或者没有人捐钱来做这件事

3 基于上面原因,记录几个好的下载资源
haha2008的696 rom在Hi!PDA论坛中
修改rom,定制rom的论坛在pdafans中
gprs cmwap上网方面的讨论在51nb论坛的网络版

4 鄙视M$一下
Vista + Windows Mobile Device Center + wm5.0 + outlook 2007 + USB cable
这是噩梦般的组合,我是无论如何不能使用usb数据线来同步outlook和wm5.0,试了一上午都没成功,下午在一个坛子上看说M$要发布新的WMDC,呵呵,不过我没兴趣再去折腾了。
补充一下,用红外线是可以同步成功的,不过那速度真是相当的慢~~~

two badly things about time difference or google

1
see my letter to Group Google BloggerDataAPI-owner:
Yesterday, I replied 2 msg to the thread "share a tool that imports
all posts from wordpress to blogger/blogspot"
however only 1 msg passed by moderator.
so I wonder the other msg is filtered by google anti-spam script or
moderator.
if it's script, that's a top priority bug to fix, I think.
if it's moderator, I wonder why it is filtered. Is it mistaken by a
duplicated one?

The following is the filtered msg. I'd like to re-publish it again.
Thanks!
"I imported about 55 posts from my local wordpress to blogger for one
time.
Today for test, I upgraded my wordpress to 2.1, created a 350kb txt
file, published all its content as a post to wordpress.
after that, python script reads all my 65 wp posts and drafts
properly.
so I wonder that's a matter of your data.
Are your wp hosted on your local Mac box?
or try to capture the response from server by httplook on a windows
box or ?? on Mac"

P.S.
I suggest this group only use a script to filter. because now I post
one msg and have to wait for 10+ hours to see it in group threads for
a time difference of 10+ hours between US and China. I guess many
other people have the same issue.
a script can work for 24 hours a day, a moderator can not. Thanks
again.


2
see google group thread or google gwt issue tracker
When I checked out google-web-toolkit source code and run the unit test, surprised to get a Failure in DateTest.java.
The reason is time difference! It only passes in GMT -1 ~ -12, and fails in GMT 0 ~ 14 countries
my fix is to comment all buggish method just like testGetHours().

the code is:
  /** Testing for public int java.util.Date.getDate()* */
  public void testGetDate() {
......
    Date accum1 = create(PAST);
    int a1 = accum1.getDate();
    assertEquals(4, a1);         ===========> it fails in GMT 0 ~ 14 countries
......
  }
.......
  Date create(String s) {
    if (s.equals(FUTURE)) {
      return new Date("12/30/2010 3:4:5 GMT");
    } else if (s.equals(PAST)) {
      return new Date("1/5/1880 GMT");
    } .......
  }

  /**
   * Testing for public int java.util.Date.getHours()
   */
  public void testGetHours() {
    // Cannot be done because each time zone will give a different
    // answer
  }

A python script that get posts from wordpress by httplib

the script can be downloaded from
http://yichao.zhang.googlepages.com/wpbyhttp.py

Readme:
# set your wordpress username, password, wp_url, wp_path
wp_username = 'admin'  
wp_passwd = 'myword'
wp_url = 'localhost' 
wp_path = '/wordpress/xmlrpc.php'
# set how many recent posts do you want to import from wordpress to blogger
wp_recent_posts_num = 5
# set filename which the wp server response will be output to
wp_response_file = 'wp_server_response.txt'


the file "wp_response_file" contains the server response. which can be found in the current folder.
if successfully, it's the posts retrieved.
if unsuccessfully, it's the debug info.

Hope this helps Erica Baker

A tool that import all posts from wordpress to blogger in python

the tool can be downloaded from
http://yichao.zhang.googlepages.com/wordpress2blogger.py

Readme:

1 set the following parameters in the wordpress2blogger.py file
# set your wordpress username, password, xmlrpc url    
wp_username = 'admin'  
wp_passwd = 'mypwd'
wp_url = 'http://localhost/wordpress/xmlrpc.php'
                       
# set your blogger email, password, blogid
# blogid is in your blogspot Dashboard url, for example:
# http://www2.blogger.com/posts.g?blogID=18083698, "18083698" is your blogid.
blogger_email = 'yichao.zhang@gmail.com'
blogger_passwd = 'mypwd'
blogger_blogid = '18083698'

# set how many recent posts do you want to import from wordpress to blogger
wp_recent_posts_num = 5 

# set debug mode = True if you want to get debug info
DEBUG = True

3 make sure python in your path

4 in terminal, type the following command
   "python wordpress2blogger.py"

js feature by jQuery copied from codeplayer

Now, after loading this page, all titles of the posts are folded and click "+" to unfold, click title to dig into the single post page.
The feature above is implemented by the following code copied from codeplayer.
css part:
.mytoggle{
  cursor:pointer;
  font-size:22px;
  color:green;
}

script part:
<script src='http://www.douban.com/js/jquery.js' type='text/javascript'></script>
<script language='javascript'>
$(function(){
  $(".post-title").prepend("<span class='mytoggle'>-</span>");
  $(".mytoggle").click(
    function(e){
      var content = $(this).parent().next().next();
      content.toggle();
      if(content.css("display")=="none")
        $(this).html("+");
      else
        $(this).html("-");
    }
  ).click();
})
</script>

Translate ipmsg into GBK on Mac OS X

在公司使用ipmsg作为即时通讯工具,不过那是windows版的,用了Mac后自然无法使用了。
最近发现不用ipmsg,好多公司的通知都收不到了,虽然偶现在实习写论文,也没什么大事儿,但还是有点不方便。
于是google之:
惊喜!ipmsg竟然有unix,Mac OS X,java等诸多版本。
立马下了个Mac的版本,装上一看,都是日文!原来ipmsg是日本人开发的,除了windows版有国人azhi做了汉化,并改名为飞鸽传书,其他版本均不支持中文。

无奈!只好试试java版的了,发现使用swt写的,于是自己汉化之。
汉化还是比较容易的,有源代码,这点上还是要赞一下那位日本作者!
主要改了3个地方,
一是把界面显示的文字汉化
二是把消息传递时的字符编码改为GBK,本想改为UTF-8的,但这样和windows版的又不兼容了,所以只好改为GBK。
三是把保存日志时的字符编码改为GBK。

另外,java版的ipmsg每次都会弹出个login的对话框,需要点一下才可以登陆,麻烦!于是改代码,把这个对话框去掉了,每次都只从ipmsg.properties文件中读取用户以及分组信息。

接下来要赞一下eclipse!
这个swt工程可以直接export成苹果的应用程序,app格式,帅呆了!
另外,程序中使用了当前目录下的文件,如icon.gif或ipmsg.properties等。通过自己建了个"./testfile.txt"的文件后,知道原来打包成.app后的当前目录就是.app所在的目录。于是把icon文件夹以及ipmsg.properties放到和ipmsg.app平级的位置就可以了!

哈哈,这下在Mac OS X下也可以使用ipmsg了,虽然java版的不支持文件传输,但以后接收公司通知是没问题了。
文件传输的问题也可以通过苹果自带的ftp服务器方便的解决。哈哈

PS - 2009-02-16:
应网友的请求, 把原来打好包的ipmsg放到了这里, 方便有需要的朋友下载
http://blogsync-java.googlecode.com/files/ipmsg-macosx.zip

另外, 如果有朋友想继续完善ipmsg for Mac OS X 的功能, 可以下载这个eclipse项目.
http://blogsync-java.googlecode.com/files/ipmsg_eclipse_project.zip

active mode OR passive mode on FTP

自从给小黑装上了Mac OS X就再也不想用windows了,不管是xp还是vista,都太蹩脚了
在公司和同事传文件时,苹果自带的ftp服务器就派上了用途。
苹果自带的这个ftp服务器还是很好用的,不需要配置,只需要在sharing中启动ftp服务,打开防火墙就OK了。
但接下来也遇到了一点点麻烦:
同事使用windows自带的ftp命令行工具则可以访问我的ftp服务器,使用flashFXP则无法访问。
后来证实是因为ftp命令行使用active mode(主动模式)所以可以访问,而flashFXP选用passive mode(被动模式)无法访问,不选用被动模式则可以正常访问。

于是google之:
看到这篇文章,疑惑顿释。
主动模式和被动模式是相对于ftp服务器来说的,即是ftp服务器主动同客户端建立通讯socket或ftp服务器被动接受客户端发来的建立通讯socket请求。
ftp以及telnet等老式的通信协议使用了2种端口,一个是命令端口(Command Port),另一个是数据端口(Data Port)
ftp的命令端口是21,这个端口在苹果防火墙设置中被开放。但另一个数据端口是在通信时由客户端随机指定的,如果使用被动模式,客户端使用数据端口进行通信时就会被Mac的防火墙拦住。而使用主动模式时,是ftp服务器使用数据端口和同事的机器建立socket,而同事机器上没有防火墙,因此可以正常访问。

后来,在同事机器上装了天网防火墙,建立了若干规则,很容易就验证了上述想法。

xmlrpc for python

使用python处理xmlrpc太简单了,又一次感受到了python的力量!
下面以使用python调用wordpress提供的xmlrpc方法为例简单介绍一下:

1) how to call xmlrpc method in python
>>> import xmlrpclib
>>> from pprint import pprint
>>> server = xmlrpclib.ServerProxy("http://localhost/wordpress/xmlrpc.php")
>>> pprint(server.system.listMethods() )

['system.multicall',
 'system.listMethods',
 'system.getCapabilities',
 'demo.addTwoNumbers',
 'demo.sayHello',
 'pingback.extensions.getPingbacks',
 'pingback.ping',
 'mt.publishPost'......]

>>> blogs = server.metaWeblog.getRecentPosts('','admin','passwd',5)
>>> pprint(blogs)
>>> print(blogs[2]['permaLink'])

http://localhost/wordpress/?p=135

2)how to setup a xmlrpc server in python

import calendar, SimpleXMLRPCServer
#The server object
class Calendar:
    def getMonth(self, year, month):
        return calendar.month(year, month)

    def getYear(self, year):
        return calendar.calendar(year)
calendar_object = Calendar()
server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 8888))
server.register_instance(calendar_object)
#Go into the main listener loop
print "Listening on port 8888"
server.serve_forever()

3)write a client to test server above
import xmlrpclib
server = xmlrpclib.ServerProxy("http://localhost:8888")
month = server.getMonth( 2002, 8 )
print month

     August 2002
Mo Tu We Th Fr Sa Su
                 1    2    3   4
 5   6   7   8    9   10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

PS:
pprint means pretty print. A cool tool too.

Reference:
1 http://docs.python.org/lib/module-xmlrpclib.html
2 http://www-128.ibm.com/developerworks/library/ws-pyth10.html
3 http://groovy.codehaus.org/XMLRPC

A simpler tool that imports all posts from wordpress to blogger/blogspot

jigar told me that the tool I post yesterday is too technical in Groups Google.
see
"""
can you make it simpler by making a standalone application. (I m trying
with that.)
This is too technical.
 If successful definitely it will be most useful thing around for
blogger.
"""
so I made it simpler by building a standalone. and
The simpler tool can be downloaded from this link.

After that, I found python is more suitable to do this staff and I'll try to make a more simpler tool written in python later.

Readme.txt
0. What is it used for?
This is a simple tool to import all posts from wordpress to blogger/blogspot.

1. How to use it?
1.1 as standalone app
for windwows user
1) set java in your path
2) set some parameters in run.bat, see details in run.bat
3) run run.bat now.

for linux/Mac OS X user
1) set java in your path
2) some parameters in run.sh, see details in run.sh
3) run run.sh now.

1.2 you can compile it and run it of course!
This tool depends on (a) Google Data APIs java client library and (b) apache xml-rpc library.
You can find them on
(a) http://code.google.com/apis/gdata/download/gdata.java.zip
(b) http://ws.apache.org/xmlrpc/download.html

download and put the above two libraries in your classpath
now configure some parameters in src/org/easter/blogsync/SyncWorker.java
1) set your wordpress blog url
2) set your wordpress blog username and password
3) set your blogger username and password
4) set your blogger blogid. Don't know what's your blogger's blogid? go to step 2.
5) set how many recent posts do you want to import from wordpress.

ok, let's run SyncWorker.java, and done.

2. what is my blogger blogid?
It's in your blogspot Dashboard url, for example: http://www2.blogger.com/posts.g?blogID=18083698
"18083698" is your blogid.


install subversion on Mac OS X

DO NOT use *.zip file, that's for windows!
decompress *.tar.gz and *-deps.tar.gz and just run:
./configure --with-apxs=/usr/local/apache2/bin/apxs --prefix=/usr/local/subversion --with-ssl --without-berkeley-db

simple shell to compress and decompress .tar.gz on Mac OS X

compress:
tar -czf dirname.tar.gz dirname

decompress:
tar -xzf dirname.tar.gz

parameter -z means .gz

A tool that imports all posts from wordpress to blogger/blogspot

The tool can be download from this link

Readme.txt:

0. What is it used for?
This is a simple tool to import all posts from wordpress to blogger/blogspot.

1. How to use it?
This tool depends on (a) Google Data APIs java client library and (b) apache xml-rpc library.
You can find them on
(a) http://code.google.com/apis/gdata/download/gdata.java.zip
(b) http://ws.apache.org/xmlrpc/download.html

download and put the above two libraries in your classpath
now configure some parameters in src/org/easter/blogsync/SyncWorker.java
1) set your wordpress blog url
2) set your wordpress blog username and password
3) set your blogger username and password
4) set your blogger blogid. Don't know what's your blogger's blogid? go to step 2.
5) set how many recent posts do you want to import from wordpress.

ok, let's run SyncWorker.java, and done.

2. what is my blogger blogid?
It's in your blogspot Dashboard url, for example: http://www2.blogger.com/posts.g?blogID=18083698

"18083698" is your blogid.

The tool can be download from this link

technorati tags:, ,

blogger/blogspot is running on tomcat 4.1.24 ?!

log in http://www.blogger.com/feeds/default/blogs

input invalid authentication info, it shows up:

BTW: Google do not have a custom error page ?!

see this image:

technorati tags:,

python.cn上的几个大牛

limodou 人称李木头,此人技术深厚,擅长写作技术blog,信仰“自己的才是大家的”!

Zoom. Quiet 人称woodpecker社区大妈,虽称大妈,不过看上去像是位艺术家,发型极具特色
http://blog.zoomquiet.org/pyblosxom/
http://www.douban.com/people/zoomq/

刘鑫 March.Liu
http://blog.csdn.net/ccat

宁柯
beyking@gmail.com
www.china-django.com

zsp(张沈鹏)

Install apache 2.0.x, mysql 5.0.*, php 5.2.0 on Mac OS X

1 install apache
./configure
make
sudo make install


apache installed on /usr/local/apache2

2 install mysql
cp -Rf mysql-5.0.* /System/Library/Frameworks/Mysql.framework/
ln -s /System/Library/Frameworks/Mysql.framework/mysql-5.0.* /usr/local/mysql
ln -s /usr/local/mysql/data /Volumes/WRK/mysql-4.1.22/data


start mysql (do NOT start as root):
/usr/local/mysql/bin/mysqld
stop mysql
/usr/local/mysql/bin/mysqladmin shutdown

3 install php
./configure --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql=/usr/local/mysql --prefix=/usr/local/php
make
sudo make install
cp src/php-5.2.0/php.ini-dist /usr/local/php/php.ini

4 config apache
vi /usr/local/apache2/conf/httpd.conf

LoadModule php5_module        modules/libphp5.so
AddType application/x-httpd-php .php .phtml
AddType application/x-httpd-php-source .phps
PHPIniDir "/usr/local/php"

DirectoryIndex index.html index.html.var index.php

ln -s /usr/local/apache2/htdocs /Volumes/WRK/Apache2/htdocs

that's all.

cnn news on 01.09

And thanks for staying with us. Here I'm Rechard Lui at the CNN center in Atlanta with your news update this hour.

A massive plan to security Baghdad said to be at the center of President Bush's new Iraq strategy. CNN has learned under this plan the capital and its neighbourhoods will be cordoned off and more US Iraqi troops will be moved in. President is expected to tell us more during the speech which is expected to happen on Wednsday at 9:00pm Eastern.

Scientists may have alternatives to a controversial embryonic stem cells. Researchers at Wake Forest and Harvard say they found stem cells in amniotic fluid donated by pregnant women. They say they've extracted them without harming the mother or the fetus. And the cells hold the same possibilities as the embryonic stem cells.

Colorado's latest snow storm is hurting rescurers' efforts to save thousands of cows stranded in the snow. Helicopters are dropping bales of hay to feed hungry cattle and an agricultural official says that about thirty five hundred cattle may have died in the state so far. He adds there that surviving cattle face the threat of lung infections from stress and dehydration.

Home Security at the port of Mami reporting all secure today. The cargo area was shut and three men were detained yesterday. The driver raised suspicions at a security gate. He did not have the right ID and he indicated he was alone and there were really three men in the truck in total. The two iraqis and a Lebanese national are in local police custody.

Today is the fifth anniversay of the "No Child Left Behind" act. Education Secretary Margaret Spellings commemorated the anniversary with the speech to education leaders this morning. The law aims to ensure that all children can do reading and math skills at grade level by 2014.

And while the college football season is ending, the NFL postseason is still going. We are down to the final 18, in the case you've been counting. It is the Eagles and the Saints on saturday night. The Bears and Seahawks will be swore to square off there on Sunday afternoon. Ah, the coaches, they will be in the place they used to call home Baltimore on Saturday afternoon. And the Patriots meet the Chargers in Santiago on Sunday. Some good matches up there.

And that's the news this hour. Remember whether you are on line or watching TV, we're your source CNN for up-to-minute headlines anytime day and night. Have a great one!