WordPress最近遇到几个安全问题

1)Froget Password Disable
据说会被Hack, 查找了一下,可以通过filter取消forget password的功能。

function remove_lostpassword_text ( $text ) {
if ($text == 'Lost your password?'){$text = '';}
return $text;
}
add_filter( 'gettext', 'remove_lostpassword_text' );


function disable_password_reset() {
return false;
}
add_filter ( 'allow_password_reset', 'disable_password_reset' );

2) 删除不需要的主题
前一阵子我的一个网站被入侵,就是wordpress默认自带的主题Twenty Eleven. Google了一下,发现是个很典型的问题,所以现在新安装的wordpress就删除这些没用的主题吧。

 

用Java实现ARP的收发

Tags: , ,

Jpcap的安装

  • 下载并安装Java Runtime.
  • 下载并安装Winpcap.
  • 下载并安装Jpcap.注意生成的jpcap.jar和jpcap.dll的文件路径。

在Eclipse中配置Jpcap

  • 创建Java Project
  • 将jpcap.jar添加到classpath中。
  • 将jpcap.dll拷贝到eclipse.exe同一目录下。


获得网络接口列表

现在的笔记本都至少有两个以上的网络接口,Jpcap只能针对一个网络接口进行网络包的监听收发,所以第一步是要了解你有哪些网络接口。这是一段现成的代码。

// Obtain the list of network interfaces
NetworkInterface[] devices = JpcapCaptor.getDeviceList();

// for each network interface
for (int i = 0; i < devices.length; i++) { // print out its name and description System.out.println(i + ": " + devices[i].name + "(" + devices[i].description + ")"); // print out its datalink name and description System.out.println(" datalink: " + devices[i].datalink_name + "(" + devices[i].datalink_description + ")"); // print out its MAC address System.out.print(" MAC address:"); for (byte b : devices[i].mac_address) System.out.print(Integer.toHexString(b & 0xff) + ":"); System.out.println(); // print out its IP address, subnet mask and broadcast address for (NetworkInterfaceAddress a : devices[i].addresses) System.out.println(" address:" + a.address + " " + a.subnet + " " + a.broadcast);

访问ARP Table

Java没有办法直接访问Windows下的ARP Table,网上推荐的方法是调用Command Line和SNMP两种。我这里使用的是前者。

final String dosCommand = “arp -a “;
try {
final Process process = Runtime.getRuntime().exec(dosCommand);
BufferedReader br = new BufferedReader(new InputStreamReader(
process.getInputStream()));
String line = null;
/*
* example:192.168.124.255 ff-ff-ff-ff-ff-ff 静态
*/
while ((line = br.readLine()) != null) {
if (line.indexOf(” ” + ip.getHostName() + ” “) > -1) {
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
// return the mac address according to the ip
return st.nextToken();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;

针对特定IP发起ARP Request

注意“captor.setFilter(“arp”, true);”的作用是过滤除了ARP之外所有的网络包,用于之后的网络包监听。

int index = Integer.parseInt(args[0]);
InetAddress targetIP = InetAddress.getByName(args[1]);
NetworkInterface device = getDevice(index);
JpcapCaptor captor = JpcapCaptor.openDevice(device, 65535, false,
20);
captor.setFilter(“arp”, true);
JpcapSender sender = captor.getJpcapSenderInstance();
// 进行广播数据报的MAC地址
byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255,
(byte) 255, (byte) 255, (byte) 255 };
// 构造REQUEST 类型的ARP的数据包
ARPPacket arp = new ARPPacket();
arp.hardtype = ARPPacket.HARDTYPE_ETHER;
arp.prototype = ARPPacket.PROTOTYPE_IP;
arp.operation = ARPPacket.ARP_REQUEST;
arp.hlen = 6;
arp.plen = 4;
// 源MAC地址
arp.sender_hardaddr = device.mac_address;
// 源IP地址
arp.sender_protoaddr = device.addresses[0].address.getAddress();
// 目地MAC地址:广播地址全为1(二进制)
arp.target_hardaddr = broadcast;
// 目地IP地址
arp.target_protoaddr = targetIP.getAddress();
// 构造以太网头部
EthernetPacket ether = new EthernetPacket();
ether.frametype = EthernetPacket.ETHERTYPE_ARP;
ether.src_mac = device.mac_address;
ether.dst_mac = broadcast;
// ARP数据包加上以网关头部
arp.datalink = ether;
// 向局域网广播ARP请求数据报
sender.sendPacket(arp);

监听ARP

Jpacap处理网络包的形式有好几种,具体可以参考官网的Tutorial.我这里使用的getPacket使用的blocking模式,所以不会同步处理包监听。
这段代码最后会判断返回的mac是否是本地缓存的,如果不是则可以认为是ARP欺骗。

while (true) {
ARPPacket packet = (ARPPacket) captor.getPacket();
if (packet != null && packet.operation == ARPPacket.ARP_REPLY) {
Inet4Address ip = (Inet4Address) packet
.getSenderProtocolAddress();
if (ip.equals(targetIP)) {
String localMac = ARP.getMacByIP(ip);
if (localMac != null ){
localMac = localMac.replaceAll(“-”, “:”);
}
if (localMac.equals(packet.getSenderHardwareAddress())) {
//Cheat
System.exit(1);
}
}
}
}

问题总结

Can’t load IA 32-bit .dll on a AMD 64-bit platform
Jpcap目前不支持64位,如果你Eclipse运行的vm是64位的,就会报这个错,下载一个32位的JRE或者JDK就可以了。


java.lang.UnsatisfiedLinkError: no jpcap in java.library.path
这是classpath中找不到DLL文件,解决的办法很多。我的做法是把jpcap.dll拷贝到eclipse.exe的文件夹下。

参考链接

WinPcap
Jpcap Download
Jpcap How to install
Jpcap 在myeclipse下的安装
JPCAP——Java中的数据链路层控制(libpcap)

 

用Filter实现GAE上的User Authentication

编者按:好吧,就是spring security的雏形概念。

Google App Engine 提供了非常详细的User Authentication的方法,详情参见Users Java API 概述。主要包括了两种方法:

在 Java 中使用用户身份验证
如果你只有单个servlet,那么很简单,在逻辑处理前调用Google提供的API(UserService)判断当前用户登录情况就可以了。

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
UserService userService = UserServiceFactory.getUserService();
String thisURL = request.getRequestURI();
if (request.getUserPrincipal() != null) {
response.getWriter().println(“<p>Hello, ” +
request.getUserPrincipal().getName() +
“! You can <a href=\”” +
userService.createLogoutURL(thisURL) +
“\”>sign out</a>.</p>”);
} else {
response.getWriter().println(“<p>Please <a href=\”” +
userService.createLoginURL(thisURL) +
“\”>sign in</a>.</p>”);
}
}
}

使用 web.xml 强制登录和管理访问
如果你的应用比较复杂,不止一个servlet,Google App Engine支持标准部署描述符web.xml中的安全约束,但是不支持自定义角色,只支持login和admin两种权限。具体示例如下

<security-constraint>
<web-resource-collection>
<url-pattern>/profile/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>

另外也可以通过google自己的部署描述符app.yaml来编写。需要注意的是App在Deploy的时候,如果应用中有app.yaml,则SDK会按照它的内容重新生成web.xml和appengine-web.xml并覆盖原来的文件。所以如果你要启用app.yaml,你需要把web.xml和appengine-web.xml中所有的内容按照YAML文件的格式全部写到app.yaml里面去。具体示例如下:

handlers:
- url: /profile/*
login: required
- url: /admin/*
servlet: com.example.AdminServlet
login: admin

编者按:需要吐槽的是app.yaml并不完全支持web.xml,至少我没有找到如何配置load-on-startup=1怎么写,所以我个人不推荐这种用法。
编者又按:app.yaml的支持功能比web.xml+appengine-web.xml还要多,所以如果你不巧正好遇到,还是得听Google的话用app.yaml.
编者最后按:我的应用比较复杂,所以Java中写code的办法没法适用,但是很努力尝试第二种方法均告失败,不管是web.xml还是app.yaml都不成功。这才总结出了上述的一些经验,以及遇到了下面两种都没有找到解法的问题。

  • 登录之后仍然显示Your client does not have permission to get URL
  • Error: User not in required role

以上两个问题均没有比较明确的解法,所以才有了本文的内容。


用Filter实现User Authentication
主要借鉴了Spring Security的思想(明明是写完才发现的。。。),然后借用了第一种方法中的code。具体的方法说起来很简单,就是在需要做权限判断的URL前添加Filter,如果权限判断失败,则跳转到Google Login的URL。先看一下实现的代码。
UserFilter.java中的实现。

@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filter)
throws IOException, ServletException {
log.info(“———–UserFilter doFilter Begin————”);
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String thisURL = request.getRequestURI();
if (request.getUserPrincipal() == null
|| (!userService.isUserLoggedIn())) {
response.setContentType(“text/html”);
response.sendRedirect(userService.createLoginURL(thisURL));
} else {
log.info(userService.getCurrentUser().getEmail());
filter.doFilter(servletRequest, servletResponse);
}
}

web.xml中相应的配置。

<filter>
<filter-name>userFilter</filter-name>
<filter-class>com.pamirhostel.booking.filters.UserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>userFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

说穿了很简单,但是基于GAE的实现略有不同,总结下来的几点经验。

  • URL最好按照功能划分子目录。例如“/cron/*”是用来实现cron job的。由于GAE的backend,cron job, task queue都是通过调用URL来实现的,所以之前喜欢把所有URI都放在Root下的习惯在这里就不适用了。
  • Filter默认不是HTTP的,所以调用request,response前需要下溯造型成HTTPRequest.
  • request.getUserPrincipal只能用来判断是否当前有Google帐号登录,这是有逻辑漏洞的。例如我的应用是For Google Apps的,所以即使当前用户登录了它自己的Google Account也是没用的。所以需要添加进一步的判断userService.isUserLoggedIn。
  • 如果多组功能需要进行用户验证,直接多添加几个filter-mapping就好了。

目前该Filter很好用,还没有什么further question,有什么问题留下评论一起讨论把。

 

WP7初体验(HTC Mozart 注册+刷机+越狱)

题外话,不得不说同样的硬件配置WP7要比Android便宜不少,看得出M记砸钱的力度,赞美一下。

前一阵子入手了一台HTC Mozart, 800万摄像头,疝气闪光灯,1.8k,再次赞美一下。友好的店主已经帮忙刷机过了,DFT(WP7的福星团队啊,资源里面第一条)出品的基于7713的ROM,已然非常的好用了。不过作为专业人士(自封的。。),还是禁不住要重头整理一遍,自己捣鼓一下。基本参考了从初见开始 Windows Phone 7 初/中/高级 经典教程专题,非常的全面,然后根据自己的情况做了一些删减,记录如下。

安装Zune
没啥好说的,看名字难道是冲着itunes来的,界面很酷,不过除了连接WP7貌似其他功能还没有兴趣了解。唯一需要注意的是如果要连接Microsfot Marketplace,一定要把电脑的区域设置成非中国。


注册Window Live ID
援引了一段原文中的话,可以了解一下WP7中的Window Live ID和Zune通行证的一个关系,总结下来就是通过Zune来注册一个新的Window Live ID(即Zune通行证)是最方便稳妥的方法。需要注意的有三点:

  1. Window Live ID中选择的区域需要和电脑的区域保持一致。
  2. Zune注册好以后需要在Window Phone上首次登录一下再与电脑进行连接,否则无法同步安装应用。
  3. Window Phone上的Window Live ID(首个)一旦登录就无法删除,除非重置手机或者刷ROM。第二个以后的Window Live ID没有这个问题。

为什么要通过向导申请ID ,这里面的奥秘,关于Windows Live ID和Zune通行证的关系(仅作了解,可以跳过):
Windows Live ID和Zune通行证有一个转换关系,当你用之前就申请好的Windows Live ID登录ZUNE的时候,你的Windows Live ID会变成一个ZUNE通行证,Windows Live ID的个人资料都会填充到ZUNE通行证里面,而且地区信息在ZUNE通行证被锁定了(无法修改,不知道为什么要这样设定),这就造成你的Windows Live ID的地区可以更改,但是ZUNE通行证的地区已经无法更改了。如果你用中国的Windows Live ID登录一次ZUNE或手机Marketplace市场,那么由于中国区不支持的原因,这个Windows Live ID就算在WP7上废了。

我们可以理解为其实WP7需要的不是Windows Live ID,而是需要Zune通行证(因为市场只认通行证的地区,却不认LIVE ID的地区),只是你在申请Zune通行证后,这个通行证会由于微软业务的共通关系同时生成一个Windows Live ID。因此为了保险起见,我们直接申请Zune通行证。



同步音乐,电影
M记的东西在这个时候用起来就会觉得极其方便了。默认配置下,只要把电影,音乐放到电脑Music/Movies目录下,就会自动显示在Zune中,拖拉一下就可以同步到Window Phone里面去了,还有比这个更方便的么。对于喜欢保存mp3的我来说,真是超级方便啊。如果不喜欢留mp3,Window Phone Marketplace里也已经有QQ音乐和虾米音乐的客户端了,赞就一个字啊。


刷机
不得不说刷机绝对是一个手艺活。是否需要越狱刷机方式不同;不同的品牌手机(HTC,三星,LG)刷机方式不同;不同的Window Phone 版本(NODO, Mango or Later)刷机方法也不同。没接触过的同学建议一定要看下面两篇文章融会贯通一下概念。

不过作为坐拥HTC Magic的我来说现在的情况已然简化很多了(骄傲),最多也就多刷一个SPL而已。。。
HTC标准的刷机方法有三种,总体的思路就是从7008之前的版本开始越狱,然后更新一次越狱一次,保证更新到最新的Mango还是越狱的。

  1. 先刷RSPL/HSPL,然后刷非官方的民间自制Mango版本的ROM。好处是快速,简单(但要谨慎),30分钟内用上Mango+越狱。
  2. 先刷7004版本的ROM,然后通过微软官方的推送更新来一步步从7004版本更新到Mango版本,更新过程中通过各种方法保留越狱状态到Mango。好处是不用担心收不到Mango以后的官方更新,且由于使用的是官方ROM,所以纯净,稳定。缺点是非常繁琐。
  3. 直接刷mango官方ROM,然后在Mango下用开发者帐号解锁。此法非常适用于二代机。

因为我有开发者帐号,无所谓这些,直接就按照第三个方法进行了刷机,非常的方便。主要刷机步骤如下,唯一要注意的是一开始连接的时候要保证机器没有锁屏。

  1. 准备好ROM,感谢DFT的努力,我们有了自己的自定义ROM,支持IE直接安装XAP这么强大的功能。不过第一次刷机我还是很胆小的准备了官方Mango 7720 ROM。
  2. 手机关机后,按住音量下键,然后同时按住开机键,进入三色屏模式,如下图,有HSPL字样,才能刷DFT或其他第三方自制 ROM,而且有这个字样的话你就不能在直接刷官方的芒果ROM,我这里刷的是7720的官方ROM
  3. 数据线接上手机,保证手机电量50%以上。电脑端开始安装驱动。如果出现找不到驱动,那就看这里解决
  4. 打开事先下载的ROM包,DFT 自制ROM与官方ROM不同的是:官方ROM直接是一个EXE文件,点击开刷;自制 ROM是个7z压缩包,需要解压它,找到ROMUpdateUtility.exe开始刷机。
  5. 然后就搞定了!



越狱
由于我莫名其妙把自己搞成了开发者(支付掉99美金。。。),所以这部就非常容易了。Microsoft默认开发者可以越狱三部设备。安装好Window Phone SDK,保证Zune运行正常,Window Phone正确连接,之后直接运行”Windows Phone Developer Registration”,输入你的开发者帐号就可以了。 复杂的注册方法,等一年之后再说吧。。。。


安装XAP
唯一的遗憾就是没有刷DFT的自定义ROM,所以没办法通过手机IE直接访问智机市场,下载XAP。
不过系统已经越狱了,除了通过Zune或者手机Marketplace来安装官方应用之外,也可以通过Window Phone SDK的Application Deployment来安装自定义的XAP应用。


遗留问题

  • MSN应用。WP7对MSN做了深度整合,所以没有单独的MSN应用。可以让我随意切换Window Live ID。
  • Skype应用。至今没有提供Skype客户端。Mango自带的Tango也没有试用成功。
  • 联系人资源。其实这是根本问题。由于绑定的Window Live ID是为了区域问题新创建的,所以没有任何联系人信息。平时用的Window Live ID可以添加但是MSN只认第一个Window Live ID。另外就是电话号码联系人会和MSN联系人混在一起有些混乱。
  • 系统通用设置。感觉少了很多系统级别通用的设置。


相关资源
论坛- 黑暗军团- DFT 论坛- Powered by Discuz!
Windows Phone 7论坛|智机网|WP7论坛|WP7软件|WP7游戏|ROM
从初见开始 Windows Phone 7 初/中/高级 经典教程专题
HTC Mozart亚太官方Mango 7720 ROM
Download Windows Phone SDK 7.1
How to: Register Your Phone for Development

 

WP-Douban-Post发布后记

距离WP-Douban-Post发布已经有差不多两个月了。总想写点什么总结一下。

缘由

说起来初衷很简单。我的博客可以同步到twitter,新浪,就差同步到豆瓣上了。受不了每次人肉同步的繁琐,又没找到合适的现成wordpress插件,又打算学习一下Oauth+Douban API + WordPress Plugin的各种技术,袖子管一卷咱就干了起来。

过程

前后大概花了一周的业余时间,大概按照如下的步骤走了一遍,总算是客服了重重难关,完成了这个插件。
1)Wordpress Hello World Plugin (了解Wordpress插件结构)
2)Wordpress Option Page(了解添加配置选项,存取数据库,添加事件响应)
3)Douban Oauth (Douban Zend Client太复杂,只好自己用php oauth library。自己拼接header,测试MD5验证,多谢Douban API支持,这部分是最艰辛的)
4)Douban API 调用(真正开始调用API,发送自己的记录到我说)
5)WP-Douban-Post (拼装完成,达达达搭)

推广

说是推广其实是抬举了自己。自己作了好用当然是想广而告之显摆一下。第一个想到的自然就是wordpress.org插件库。如果可以在Plugins里面搜索到自己的名字,想想就威风。
提交开发者申请,编写README,提交代码。所有的步骤都有清楚的指南。Wordpress.org基于SVN的发布系统让我大开眼界,收益非浅。
1)通过README提取插件信息,例如作者信息,安装指南,Q&A,对开发人员和wordpress.org都是方便清楚。
2)建议开发者拉branch进行发布,通过README元信息指定发布的具体branch.通过这样的最佳实践直接帮助开发者分清了发布和开发的代码。清晰明了。

然后就是更新豆瓣应用信息,在豆瓣API小组发贴庆祝。然后就是每天刷屏期待了。。。这就是我做的推广-,-。大概的情形趋势图上都清清楚楚。
1)2011-01-19,推荐给了几个用豆瓣也用Wordpress的朋友,小推销了一把,迎来了下载高峰。
2)2011-01-21 to 2011-02-12,春节期间,下载量逐渐走低。。。。
3)2011-02-13 to 2011-02-20,春节结束,赢了小小的反弹。。。。
4)2011-02-21 to 2011-03-20, 大概可以看出周末基本没有下载,工作日偶尔有人。。
5)2011-03-25, 豆瓣有了应用小站,很荣幸被发布了出来,还被表扬了^@^,也迎来了之后小小的高峰。。。

没有什么特别的期待,有人用,觉得有用大概就是对我最大的夸奖了。呵呵。欢迎提意见给我。

后记

1)听说豆瓣九点可以自动收录wordpress,我那叫一个后悔啊。新手不会玩豆瓣,sigh。
2)想要php调用douban Oauth的同学,可以参考我在wordpress.org上的代码,这可是汗水和泪水啊-,-
3)wordpress.org的缺点也很明显,一定需要wordpress.com的账号才能rate和vote,这导致基本没人回来给意见。

WordPress.org 地址

http://wordpress.org/extend/plugins/wp-douban-post/stats/

Douban应用小站 地址

http://site.douban.com/widget/notes/2324209/note/141570144/

WP-Douban-Post Stats 2011-03-26

WP-Douban-Post Stats 2011-03-26

 

在Linode上安装openVPN

一些朋友需要VPN访问一些墙外资源。本着服务器放着不用是浪费的原则我决定搭一个VPN服务器帮助解决朋友的需求。
不得不说Linode的文档非常齐全,而且很傻瓜,照着作一遍基本解决了所有的问题。

openVPN安装

Ubuntu方便是没话说的。

apt-get update
apt-get upgrade --show-upgraded
apt-get install openvpn udev

这就是所有你需要的软件了。安装包里提供了密钥工具包“easy-rsa”,把它拷贝到配置文件夹下。

cp -R /usr/share/doc/openvpn/examples/easy-rsa/ /etc/openvpn

修改工具包中的文件vars,按照你的需要配置相关信息

export KEY_COUNTRY="US"
export KEY_PROVINCE="OH"
export KEY_CITY="Oxford"
export KEY_ORG="Ducklington"
export KEY_EMAIL="squire@ducklington.org"

配置你的密钥体系

首先需要初始化PKI.

cd /etc/openvpn/easy-rsa/2.0/
source . /etc/openvpn/easy-rsa/2.0/vars
. /etc/openvpn/easy-rsa/2.0/clean-all
. /etc/openvpn/easy-rsa/2.0/build-ca

接着生成服务器私钥。会有一些问题和提示,选择默认和Y就可以了。

. /etc/openvpn/easy-rsa/2.0/build-key-server server

然后是生成客户端的证书.其中client1是证书的名字,你可以按照你的需要填写。如果需要生成多个客户端证书,重复该步骤就可以了。

. /etc/openvpn/easy-rsa/2.0/build-key client1

最后生成Diffie Hellman Parameters。

. /etc/openvpn/easy-rsa/2.0/build-dh
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time

如果一切正常,会出现如上所示的信息。
密钥的配置基本就是这样,最后把生成的密钥文件和easy-rsa提供的服务器端配置文件样本拷贝到配置文件夹下。

cd /etc/openvpn/easy-rsa/2.0/keys
cp ca.crt ca.key dh1024.pem server.crt server.key /etc/openvpn

cd /usr/share/doc/openvpn/examples/sample-config-files
gunzip -d server.conf.gz
cp server.conf /etc/openvpn/
cp client.conf ~/
cd ~/

配置openVPN链接

密钥的配置保证了客户端和VPN服务器的链接。还要配置openVPN对网络包地转发规则。
修改/etc/openvpn/server.conf,添加如下语句

push "redirect-gateway def1"

修改/etc/sysctl.conf,添加如下语句

net.ipv4.ip_forward=1

为VPN会话添加下列变量。

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 10.8.0.0/24 -j ACCEPT
iptables -A FORWARD -j REJECT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

为了保证每次启动这边规则都生效,可以把后面四个语句添加到/etc/rc.local中去。
除了IP包地转发规则,我们还需要处理来自客户端的dns请求,安装dnsmasq来解决这个问题。

apt-get update
apt-get install dnsmasq

修改/etc/openvpn/server.conf,添加如下语句

push "dhcp-option DNS 10.8.0.1"

OK,一切搞定,启动openVPN服务吧。

/etc/init.d/openvpn restart

客户端的使用

Mac下的openVPN客户端
http://code.google.com/p/tunnelblick/
Windows下的openVPN客户端
http://openvpn.se/download.html
将上文生成的client.conf, ca.crt, client1.key, client1.crt拷贝到你的电脑,配合客户端使用即可。

参考连接

http://library.linode.com/networking/openvpn/ubuntu-10.04-lucid

推荐连接

有兴趣的朋友可以使用我的referral链接注册使用Linode,将获得10%的优惠。
http://www.linode.com/?r=513a31440dd0a6c0980cc05a164b4ae2520a19d6

 

WordPress搬家记

Hostmonster的两年租约转眼到期了。想当年4刀每月如今已经涨到了6刀每月,感慨之际只好把东西通通往Linode这边搬。希望这次可以坚持的更长。
Hostmonster上需要搬的也就一个wordpress,其中有经历了一些问题,记录下来也算是经验。
之前已经把domain transfer搞定了,所以主要就是程式的挪动。大致的搬家过程主要分三步。

数据库搬家

不得不说mysqldump还是很方便的。对于我这种要求不高,只求没数据遗留的人来说超级方便。

mysqldump wordpress > wordpress.sql

然后在linode上直接导入数据库就好了

mysql -u xxx -p
create database wordpress character set utf8;
exit
mysql -u xxx -p -D wordpress < wordpress.sql

WordPress搬家

我的hostmonster和Linode都开通了SSH服务。所以php文件搬家直接就是一句话的事情。

scp -rf blog/* xxx@tech4k.com:/path/

配置更新

wp-configs.php包含了wordpress的所有全局配置,搬过来以后要做对应的修改。我的域名没有变,所以只需要对数据库配置修改就可以了。

define('DB_NAME', 'xxxxxx');^M
^M
/** MySQL database username */^M
define('DB_USER', 'xxxxxx');^M
^M
/** MySQL database password */^M
define('DB_PASSWORD', 'xxxxxx');^M
^M
/** MySQL hostname */^M
define('DB_HOST', 'xxxxxxxx');^M



除了这三步之外还有一些小地方的注意事项,也许不适合你的案例,但是注意一下总归没错。

用户和用户组,以及权限

scp搬过来的所有文件的用户和用户组信息都会被修改,一开始完全没有办法更新插件。之后才发现这个问题的。这里的用户必须是apache的执行用户,这样才不会有问题。

chown -R aaa:xxx *

设置上传路径

在wordpress的管理界面中,需要设置一个上传路径,这样才可以自动从wordpress.org上自动下载插件。
Settings -> Media -> Store uploads in this folder
需要注意的是该文件夹需要权限配置可写。

sitemap.xml&sitemap.xml.gz配置

我使用了XML-Sitemap这个插件,方便好用。该插件需要根目录下的sitemap.xml和sitemap.xml.gz可写。

遗留问题

timer启动

理论上有些插件需要在插件激活的时候启动一些后台Timer定时完成任务的。不是很清楚我这样的搬家方法会不会对这种插件有问题。

Permalink配置

我现在的Permalink有问题。只能通过/?p=%post_id%来访问,不能通过/archive/%post_id%这种方式访问。添加了.htaccess也确认可写之后仍然有这个问题。还需要进一步研究。

搬家总是很麻烦,皑皑,感叹一下。

 

WordPress Ajax Widget开发

本篇文章是紧接上一篇WordPress 2.8+ Widget 插件开发.
自己开发得一个小工具 WP Dict需要调用服务器端方法查询单词,但是个人觉得页面跳转的用户体验不好,所以小研究了一把ajax的实现方法而且试验成功,特此记录。

注册服务端方法


function wp_dict_request_handler() {
}
add_action('init', 'wp_dict_request_handler');

比想象的更加简单,Wordpress可以通过add_action注册自己的服务器端方法,所有页面均可调用。唯一需要注意的是方法名需要保证唯一。

注册jquery

在Widget类的构造方法中添加如下语句。

// Load jQuery
wp_enqueue_script('jquery');

获得Widget ID

在widget()方法extract方法后天添加如下语句。

function widget( $args, $instance ) {
extract( $args );
// Get the div id of the widget
$widgetid = $args['widget_id'];
。。。
}

Ajax实现

在Widget的显示内容部分($after_title,$after_widget之间)添加ajax调用代码。

echo $after_title;
?>
<script type="text/javascript">
jQuery(document).ready(function($){
$("#wp_dict_submit").click(function(){
$.ajax({
type : "GET",
url : "index.php",
data : { mywidget_request : "wp_dict_request_handler",
query : $("#wp_dict_query").val() },
success : function(response) {
// The server has finished executing PHP and has returned something,
// so display it!
$("#wp_dict_result").html(response);
}
});
});
});
</script>
<?php

/* After widget (defined by themes). */
echo $after_widget;

基本就是jQuery调用ajax的典型用法。不过这里有几点针对wordpress的说明

  • url参数其实可以是任意有效的php路径。
  • mywidget_request配置的就是之前注册的方法名。
  • mywidget_request之后的都是自定义的参数,这里我需要一个query的单词名。

最后

按照普通Widget的要求,把widget添加之后,就可以在页面看到并使用了。相当方便。

遗留问题

WP Super Cache会把页面内容转化成静态HTML,所以Ajax会破坏这个机制。参考资料中另外一篇文章,不过还没有时间学习。

参考资料

AJAX-ifying WordPress Widgets
Make Any Plugin Work with WP Super Cache

 

WordPress 2.8+ Widget 插件开发

WordPress 2.8.0之后的版本对Widget提供了一套详细的API规范。貌似和水煮鱼团队的范例有很大区别。

加载Widget


<?php

/* Add our function to the widgets_init hook. */
add_action( 'widgets_init', 'example_load_widgets' );

/* Function that registers our widget. */
function example_load_widgets() {
register_widget( 'Example_Widget' );
}

Wordpress 2.8以上的版本中,widgets_init事件会在wordpress注册默认widgets之后被触发。Example_Widget是具体实现widget的类名。

配置你的Widget


function Example_Widget() {
/* Widget settings. */
$widget_ops = array( 'classname' => 'example', 'description' => 'An example widget that displays a person\'s name and sex.' );

/* Widget control settings. */
$control_ops = array( 'width' => 300, 'height' => 350, 'id_base' => 'example-widget' );

/* Create the widget. */
$this->WP_Widget( 'example-widget', 'Example Widget', $widget_ops, $control_ops );
}

这里主要配置的Widget的一些基本信息,例如标识符,描述,以及大小等信息。

显示你的Widget


function widget( $args, $instance ) {
extract( $args );

/* User-selected settings. */
$title = apply_filters('widget_title', $instance['title'] );
$name = $instance['name'];
$sex = $instance['sex'];
$show_sex = isset( $instance['show_sex'] ) ? $instance['show_sex'] : false;

/* Before widget (defined by themes). */
echo $before_widget;

/* Title of widget (before and after defined by themes). */
if ( $title )
echo $before_title . $title . $after_title;

/* Display name from widget settings. */
if ( $name )
echo '<p>Hello. My name is' . $name . '.</p>';

/* Show sex. */
if ( $show_sex )
echo '<p>I am a ' . $sex . '.</p>';

/* After widget (defined by themes). */
echo $after_widget;
}

Wordpress 2.8以上的版本中,规定了function widget( $args, $instance )这个接口。这个接口专门用来实现widget的显示内容的。其中extract( $args );是标准用法,只需要添加就可以了。除此之外Wordpress还提供了四个标准变量用来区分显示的阶段:$before_widget, $after_widget, $before_title, and $after_title。最后$instance是用来获取Widget本身实例属性的。Widget实例属性的操作会在后面提到。

Widget配置页面

在Widgets页面中添加了某个Widget之后可以配置该Widget的实例属性。

function form( $instance ) {

/* Set up some default widget settings. */
$defaults = array( 'title' => 'Example', 'name' => 'John Doe', 'sex' => 'male', 'show_sex' => true );
$instance = wp_parse_args( (array) $instance, $defaults ); ?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>">Title:</label>
<input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" value="<?php echo $instance['title']; ?>" style="width:100%;" />
</p>
<p>
<label for="<?php echo $this->get_field_id( 'name' ); ?>">Your Name:</label>
<input id="<?php echo $this->get_field_id( 'name' ); ?>" name="<?php echo $this->get_field_name( 'name' ); ?>" value="<?php echo $instance['name']; ?>" style="width:100%;" />
</p>
<p>
<label for="<?php echo $this->get_field_id( 'sex' ); ?>">Sex:</label>
<select id="<?php echo $this->get_field_id( 'sex' ); ?>" name="<?php echo $this->get_field_name( 'sex' ); ?>" class="widefat" style="width:100%;">
<option <?php if ( 'male' == $instance['format'] ) echo 'selected="selected"'; ?>>male</option>
<option <?php if ( 'female' == $instance['format'] ) echo 'selected="selected"'; ?>>female</option>

</p>
<p>
<input class="checkbox" type="checkbox" <?php checked( $instance['show_sex'], true ); ?> id="<?php echo $this->get_field_id( 'show_sex' ); ?>" name="<?php echo $this->get_field_name( 'show_sex' ); ?>" />
<label for="<?php echo $this->get_field_id( 'show_sex' ); ?>">Display sex publicly?</label>
</p>
<?php
}
?>

Wordpress提供了标准接口function form( $instance ) 用来显示该配置页面的内容。方法中主要包含两个部分。

  • 第一部分配置了所有实例属性的默认值。
  • 第二个部分是配置页面的显示内容。

更新Widget实例属性


function update( $new_instance, $old_instance ) {
$instance = $old_instance;

/* Strip tags (if needed) and update the widget settings. */
$instance['title'] = strip_tags( $new_instance['title'] );
$instance['name'] = strip_tags( $new_instance['name'] );
$instance['sex'] = $new_instance['sex'];
$instance['show_sex'] = $new_instance['show_sex'];

return $instance;
}

Wordpress提供了标准接口function update( $new_instance, $old_instance )用来处理实例属性的更新。更新的内容直接设置在$instance中就可以了。不需要直接操作数据库。

总结

这就是所有开发正常widget所需要了解的内容了。快动手吧。

参考资料

http://codex.wordpress.org/Widgets_API
The complete guide to creating widgets in WordPress 2.8

 

WP-Dict version 1.0.0 released

=== Plugin Name ===
Contributors: windlx
Donate link: http://blog.tech4k.com/
Tags: dict, translation, wp-dict
Requires at least: 2.8
Tested up to: 3.0.4
Stable tag: 1.0.0

Widget Plugin. A widget could help to translate between English and Chinese.Take advantage of the API from dict.cn.

== Description ==

Widget Plugin. A widget could help to translate between English and Chinese.Take advantage of the API from dict.cn. Implimented by Ajax.

== Installation ==

  1. Unzip wp-douban-post to the ‘/wp-content/plugins/’ directory
  2. Activate the plugin through the ‘Plugins’ menu in WordPress
  3. Add ‘WP Dict’ in through the ‘Widgets’ menu in WordPress
  4. Any problem, please contact windlx from my blog.
  1. 解压后将文件上传到’/wp-content/plugins/’目录
  2. 在插件中激活wp-dict
  3. 在Widget中添加WP-Dict
  4. 如有问题,请通过我的博客windlx联系我

== Frequently Asked Questions ==

= What do I need before the installation? =

You don’t need anything. Dict.cn API is open to everyone.

= Why I can’t see WP Dict Widget from my ‘Widgets’? =

There is a build issue before 2011/01/23. If you used to install it, please delete the plugin and reinstall it. Everything will be fine.

== Screenshots ==

1. WP-Dict

== Changelog ==

= 1.0.0 =
Get the Translation of the words by Ajax.

== Upgrade Notice ==

= 1.0.0 =
Basic Version.

 

无觅相关文章插件,快速提升流量