java导入163联系人

现在在许多SNS中都有导入邮箱联系人的功能,以下的代码功能就是如何取得163邮箱账号的联系人
更多的导入联系人代码,可在此处下载:http://code.google.com/p/jcontactfetcher/

首先是工具类 
1.CommonUtil类,现在只有初始化log4j方法

[java]  view plain copy
  1. package org.gc.contact.util;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.util.Properties;  
  5. import org.apache.log4j.PropertyConfigurator;  
  6. public final class CommonUtil {  
  7.       
  8.     static {  
  9.         initLog4J();  
  10.     }  
  11.       
  12.     private CommonUtil() {}  
  13.       
  14.     private static void initLog4J() {  
  15.          InputStream in = CommonUtil.class.getResourceAsStream("/log4j.properties");  
  16.          Properties props = new Properties();  
  17.             try {  
  18.                 props.load(in);  
  19.                 PropertyConfigurator.configure(props);  
  20.             } catch(IOException ex) {  
  21.                 System.out.println("Log4JLogger Create Exception:/n" + ex.getMessage());  
  22.             }  
  23.     }  
  24.       
  25. }     

2.EncodingUtil类,编码相关
[java]  view plain copy
  1. package org.gc.contact.util;  
  2. public class EncodingUtil {  
  3.       
  4.     public static final String DEFAULT_ENCODING = "UTF-8";  
  5.       
  6.     private EncodingUtil() {}  
  7.       
  8. }  

3.RegexpUtil类,正则表达式工具
[java]  view plain copy
  1. package org.gc.contact.util;  
  2. import java.util.regex.Pattern;  
  3. public final class RegexpUtil {  
  4.       
  5.     private static final String REGEXP_FORM_INPUT_VALUE = "<//s*input(?://s+.*)*//s+value=/"?([^//s/"]*)/"?";  
  6.     private static final String REGEXP_HOST_FROM_URI = "http://([^/]+)/.*";  
  7.     private static final String REGEXP_JS_REDIRECTION = "location//.replace//(/"(.+)/"//)";  
  8.       
  9.     private static final Pattern PATTERN_FORM_INPUT_VALUE = Pattern.compile(REGEXP_FORM_INPUT_VALUE);  
  10.     private static final Pattern PATTERN_HOST_FROM_URI = Pattern.compile(REGEXP_HOST_FROM_URI);  
  11.     private static final Pattern PATTERN_JS_REDIRECTION = Pattern.compile(REGEXP_JS_REDIRECTION);  
  12.       
  13.       
  14.     private RegexpUtil() {}  
  15.       
  16.     public static Pattern getFormInputValueRegexp() {  
  17.         return PATTERN_FORM_INPUT_VALUE;  
  18.     }  
  19.       
  20.     public static Pattern getHostFromURIRegexp() {  
  21.         return PATTERN_HOST_FROM_URI;  
  22.     }  
  23.       
  24.     public static Pattern getJSRedirectionRegexp() {  
  25.         return PATTERN_JS_REDIRECTION;  
  26.     }  
  27. }  


接着是实体类 
4.联系人类
[java]  view plain copy
  1. package org.gc.contact.model;  
  2. import java.io.Serializable;  
  3. public class ContactInfo implements Serializable {  
  4.     private static final long serialVersionUID = 5530203198421462152L;  
  5.     private final String username;  
  6.     private final String address; //email & qq etc.  
  7.       
  8.     public ContactInfo(String username, String address) {  
  9.         this.username = username;  
  10.         this.address = address;  
  11.     }  
  12.       
  13.     public String getUsername() {  
  14.         return username;  
  15.     }  
  16.       
  17.     public String getAddress() {  
  18.         return address;  
  19.     }  
  20.       
  21.     public String toString() {  
  22.         return "[" + username + "," + address + "]";  
  23.     }  
  24.       
  25. }  


异常类 
5.错误消息
[java]  view plain copy
  1. package org.gc.contact.exception;  
  2. public final class ExceptionMessage {  
  3.       
  4.     public static final String PROTOCOL_MSG = "Don't find a match; may be the regular expression error, may be the protocol changed.";  
  5.       
  6.     private ExceptionMessage() {}  
  7.       
  8. }  

6.HttpBrowserException,浏览器异常
[java]  view plain copy
  1. package org.gc.contact.exception;  
  2. @SuppressWarnings("serial")  
  3. public class HttpBrowserException extends Exception {  
  4.       
  5.     public HttpBrowserException() {  
  6.         super();  
  7.     }  
  8.       
  9.     public HttpBrowserException(String error) {  
  10.         super(error);  
  11.     }  
  12.       
  13.     public HttpBrowserException(Throwable t) {  
  14.         super(t);  
  15.     }  
  16.       
  17.     public HttpBrowserException(String error, Throwable t) {  
  18.         super(error, t);  
  19.     }  
  20.       
  21. }  

7.HttpServerException,服务器异常
[java]  view plain copy
  1. package org.gc.contact.exception;  
  2. @SuppressWarnings("serial")  
  3. public class HttpServerException extends Exception {  
  4.       
  5.     public HttpServerException() {  
  6.         super();  
  7.     }  
  8.       
  9.     public HttpServerException(String error) {  
  10.         super(error);  
  11.     }  
  12.       
  13.     public HttpServerException(Throwable t) {  
  14.         super(t);  
  15.     }  
  16.       
  17.     public HttpServerException(String error, Throwable t) {  
  18.         super(error, t);  
  19.     }  
  20.       
  21. }  

8.ProtocolException,协议异常
[java]  view plain copy
  1. package org.gc.contact.exception;  
  2. @SuppressWarnings("serial")  
  3. public class ProtocolException extends Exception {  
  4.       
  5.     public ProtocolException() {  
  6.         super();  
  7.     }  
  8.       
  9.     public ProtocolException(Throwable t) {  
  10.         super(t);  
  11.     }  
  12.       
  13.     public ProtocolException(String error) {  
  14.         super(error);  
  15.     }  
  16.       
  17.     public ProtocolException(String error, Throwable t) {  
  18.         super(error, t);  
  19.     }  
  20.       
  21. }  


核心类 
9.HttpBrowser类,模拟浏览器的类,有get,post行为
[java]  view plain copy
  1. package org.gc.contact.http;  
  2. import java.io.IOException;  
  3. import java.util.Date;  
  4. import java.util.regex.Matcher;  
  5. import java.util.regex.Pattern;  
  6. import org.apache.commons.httpclient.Cookie;  
  7. import org.apache.commons.httpclient.Header;  
  8. import org.apache.commons.httpclient.HttpClient;  
  9. import org.apache.commons.httpclient.HttpException;  
  10. import org.apache.commons.httpclient.HttpMethod;  
  11. import org.apache.commons.httpclient.HttpStatus;  
  12. import org.apache.commons.httpclient.NameValuePair;  
  13. import org.apache.commons.httpclient.URIException;  
  14. import org.apache.commons.httpclient.cookie.CookiePolicy;  
  15. import org.apache.commons.httpclient.methods.GetMethod;  
  16. import org.apache.commons.httpclient.methods.PostMethod;  
  17. import org.apache.commons.httpclient.params.HttpMethodParams;  
  18. import org.apache.commons.httpclient.protocol.Protocol;  
  19. import org.apache.commons.logging.Log;  
  20. import org.apache.commons.logging.LogFactory;  
  21. import org.gc.contact.exception.ExceptionMessage;  
  22. import org.gc.contact.exception.HttpBrowserException;  
  23. import org.gc.contact.exception.HttpServerException;  
  24. import org.gc.contact.util.EncodingUtil;  
  25. import org.gc.contact.util.RegexpUtil;  
  26. @SuppressWarnings("deprecation")  
  27. public class HttpBrowser {  
  28.       
  29.     static {  
  30.         SSLUtilities.trustAllHttpsCertificates();  
  31.         SSLUtilities.trustAllHostnames();  
  32.         Protocol myhttps = new Protocol("https"new SSLFactory (), 443);  
  33.         Protocol.registerProtocol("https", myhttps);  
  34.     }  
  35.       
  36.     private static final Log logger = LogFactory.getLog(HttpBrowser.class);  
  37.       
  38.     private HttpClient httpClient;  
  39.     protected String encoding;  
  40.     protected int bufferLength = 4096;  
  41.     protected String lastVisitUrl;  
  42.       
  43.       
  44.     public HttpBrowser() {  
  45.         this(EncodingUtil.DEFAULT_ENCODING);  
  46.     }  
  47.       
  48.     public HttpBrowser(String encoding) {  
  49.         this.encoding = encoding;  
  50.         httpClient = new HttpClient();  
  51.         httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);  
  52.         httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, encoding);  
  53.         httpClient.getParams().setParameter(HttpMethodParams.SINGLE_COOKIE_HEADER, true);  
  54.     }  
  55.       
  56.     //----------- callable methods -------------//  
  57.     public HttpResponse doGet(String url) throws HttpBrowserException, HttpServerException {  
  58.         return doGet(url, "");  
  59.     }  
  60.       
  61.     public HttpResponse doGet(String url, String referer) throws HttpBrowserException, HttpServerException {  
  62.         try {  
  63.             GetMethod getMethod = new GetMethod(url);  
  64.             setHttpRequestHeader(getMethod);  
  65.             if (referer != null && referer.trim().length() != 0) {  
  66.                 getMethod.setRequestHeader("Referer", referer);  
  67.             }  
  68.             logHttpGetRequest(getMethod);  
  69.             int status =httpClient.executeMethod(getMethod);  
  70.             String strResp = getMethod.getResponseBodyAsString();  
  71.             byte[] byteResp = getMethod.getResponseBody();  
  72.             String respEnc = getResponseEncoding(getMethod);  
  73.             logHttpResponse(getMethod, strResp);  
  74.             getMethod.releaseConnection();  
  75.               
  76.             //http:301,302,303,307  
  77.             if (status == HttpStatus.SC_MOVED_PERMANENTLY ||   
  78.                 status == HttpStatus.SC_MOVED_TEMPORARILY ||  
  79.                 status == HttpStatus.SC_SEE_OTHER ||  
  80.                 status == HttpStatus.SC_TEMPORARY_REDIRECT) {  
  81.                 Header locationHeader = getMethod.getResponseHeader("Location");  
  82.                 String location = locationHeader.getValue();  
  83.                 if (logger.isDebugEnabled()) {  
  84.                     logger.debug("Redirect To Location = " + location);  
  85.                 }  
  86.                 if (location.startsWith("http")) {  
  87.                     return doGet(location);  
  88.                 } else {  
  89.                     return doGet("http://" + getResponseHost(getMethod) + location);  
  90.                 }  
  91.             } else if (status == HttpStatus.SC_OK) { //http:200  
  92.                 return new HttpResponse(getMethod.getURI().toString(), byteResp, respEnc);  
  93.             } else {  
  94.                 throw new HttpServerException("Server Exception[code=" + status + "]");  
  95.             }  
  96.         } catch (HttpException e) {   
  97.             throw new HttpBrowserException(e);  
  98.         } catch (IOException e) {  
  99.             throw new HttpBrowserException(e);  
  100.         }  
  101.     }  
  102.       
  103.     public HttpResponse doPost(String url, NameValuePair[] params) throws HttpBrowserException, HttpServerException {  
  104.         return doPost(url, params, "");  
  105.     }  
  106.       
  107.     public HttpResponse doPost(String url, NameValuePair[] params, String referer) throws HttpBrowserException, HttpServerException {  
  108.         try {  
  109.             PostMethod postMethod = new PostMethod(url);  
  110.             setHttpRequestHeader(postMethod);  
  111.             if (referer != null && referer.trim().length() != 0) {  
  112.                 postMethod.setRequestHeader("Referer", referer);  
  113.             }  
  114.             postMethod.setRequestHeader("Content-Type""application/x-www-form-urlencoded");  
  115.             postMethod.setRequestBody(params);  
  116.             logHttpPostRequest(postMethod);  
  117.             int status = httpClient.executeMethod(postMethod);  
  118.             String strResp = postMethod.getResponseBodyAsString();  
  119.             byte[] byteResp = postMethod.getResponseBody();  
  120.             String respEnc = getResponseEncoding(postMethod);  
  121.             logHttpResponse(postMethod, strResp);  
  122.             postMethod.releaseConnection();  
  123.               
  124.             //http:301,302,303,307  
  125.             if (status == HttpStatus.SC_MOVED_PERMANENTLY ||   
  126.                 status == HttpStatus.SC_MOVED_TEMPORARILY ||  
  127.                 status == HttpStatus.SC_SEE_OTHER ||  
  128.                 status == HttpStatus.SC_TEMPORARY_REDIRECT) {  
  129.                 Header locationHeader = postMethod.getResponseHeader("Location");  
  130.                 String location = locationHeader.getValue();  
  131.                 if (logger.isDebugEnabled()) {  
  132.                     logger.debug("Redirect To Location = " + location);  
  133.                 }  
  134.                 if (location.startsWith("http")) {  
  135.                     return doGet(location);  
  136.                 } else {  
  137.                     return doGet("http://" + getResponseHost(postMethod) + location);  
  138.                 }  
  139.             } else if (status == HttpStatus.SC_OK) { //http:200  
  140.                 return new HttpResponse(postMethod.getURI().toString(), byteResp, respEnc);  
  141.             } else {  
  142.                 throw new HttpServerException("Server Exception[code=" + status + "]");  
  143.             }  
  144.         } catch (HttpException e) {   
  145.             throw new HttpBrowserException(e);  
  146.         } catch (IOException e) {  
  147.             throw new HttpBrowserException(e);  
  148.         }  
  149.     }  
  150.       
  151.     public Cookie[] getCurrentCookies() {  
  152.         Cookie[] cookies = httpClient.getState().getCookies();  
  153.         return cookies;  
  154.     }  
  155.       
  156.     protected void setHttpRequestHeader(HttpMethod method) {  
  157.         method.setRequestHeader("Accept",  
  158.                 "text/html,application/xhtml+xml,application/xml,application/json,image/jpeg,image/gif,*/*");  
  159.         method.setRequestHeader("Accept-Language""zh-cn");  
  160.         method.setRequestHeader(  
  161.                 "User-Agent",  
  162.                 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3");  
  163.         method.setRequestHeader("Accept-Charset", encoding);  
  164.         method.setRequestHeader("Keep-Alive""300");  
  165.         method.setRequestHeader("Connection""Keep-Alive");  
  166.         method.setRequestHeader("Cache-Control""no-cache");  
  167.     }  
  168.       
  169.     //------------- log methods ----------------//  
  170.     private void logHttpGetRequest(HttpMethod method) {  
  171.         try {  
  172.             if (logger.isDebugEnabled()) {  
  173.                 logger.debug("/n/n============= HTTP Request Start =============");  
  174.                 logger.debug("HTTP Get Request URL ==>/n" + method.getURI().toString());  
  175.                 logger.debug("HTTP Get Request Headers ==>/n" + getHttpRequestHeader(method));  
  176.                 logger.debug("HTTP Get Request Cookies ==>/n" + getHttpCookie());  
  177.                 logger.debug("HTTP Get Request QueryString ==>/n" + method.getQueryString());  
  178.                 logger.debug("============= HTTP Request End =============/n/n");  
  179.             }  
  180.         } catch (URIException e) {  
  181.             logger.error(e);  
  182.         }  
  183.     }  
  184.       
  185.     private void logHttpPostRequest(PostMethod method) {  
  186.         try {  
  187.             if (logger.isDebugEnabled()) {  
  188.                 logger.debug("/n/n============= HTTP Request Start =============");  
  189.                 logger.debug("HTTP Post Request URL ==>/n" + method.getURI().toString());  
  190.                 logger.debug("HTTP Post Request Headers ==>/n" + getHttpRequestHeader(method));  
  191.                 logger.debug("HTTP Post Request Cookies ==>/n" + getHttpCookie());  
  192.                 logger.debug("HTTP Post Request QueryString ==>/n" + method.getQueryString());  
  193.                 logger.debug("HTTP Post Request Body ==>/n" + getHttpRequestBody(method));  
  194.                 logger.debug("============= HTTP Request End =============/n/n");  
  195.             }  
  196.         } catch (URIException e) {  
  197.             logger.error("URIException", e);  
  198.         }  
  199.     }  
  200.       
  201.     private void logHttpResponse(HttpMethod method, String strResp) {  
  202.         try {  
  203.             if (logger.isDebugEnabled()) {  
  204.                 logger.debug("/n/n============= HTTP Response Start =============");  
  205.                 logger.debug("HTTP Response URL ==>/n" + method.getURI().toString());  
  206.                 logger.debug("HTTP Response Headers ==>/n" + getHttpResponseHeader(method));  
  207.                 logger.debug("HTTP Response Cookies ==>/n" + getHttpCookie());  
  208.                 logger.debug("HTTP Response Body ==>/n" + strResp);  
  209.                 logger.debug("============= HTTP Response End =============/n/n");  
  210.             }  
  211.         } catch (URIException e) {  
  212.             logger.error("URIException", e);  
  213.         }  
  214.     }  
  215.       
  216.     //---------- util methods -------------//  
  217.     private String getResponseEncoding(HttpMethod method) {  
  218.         Header header = method.getResponseHeader("Content-Type");  
  219.         String encoding = EncodingUtil.DEFAULT_ENCODING;  
  220.         if (header != null) {  
  221.             if (logger.isDebugEnabled()) {  
  222.                 logger.debug("Content-Type=" + header.getValue());  
  223.             }  
  224.             if (header != null) {  
  225.                 String value = header.getValue();  
  226.                 int idx1 = value.indexOf("charset=");  
  227.                 if (idx1 > -1) {  
  228.                     encoding = value.substring(idx1 + 8);  
  229.                     if (logger.isDebugEnabled()) {  
  230.                         logger.debug("Response Encoding=" + encoding);  
  231.                     }  
  232.                 }  
  233.             }  
  234.         }  
  235.         return encoding;  
  236.     }  
  237.       
  238.     private String getResponseHost(HttpMethod method) {  
  239.         try {  
  240.             Pattern p = RegexpUtil.getHostFromURIRegexp();  
  241.             String url = method.getURI().toString();  
  242.             Matcher matcher = p.matcher(url);  
  243.             if (!matcher.find()) {  
  244.                 String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=ResponseHost,Regexp=" + p.pattern() + "]";  
  245.                 logger.error(msg);  
  246.             }  
  247.             if (logger.isDebugEnabled()) {  
  248.                 logger.debug("Host=" + matcher.group(1));  
  249.             }  
  250.             return matcher.group(1);  
  251.         } catch (Exception e) {  
  252.             logger.error(e);  
  253.         }  
  254.         return "";  
  255.     }  
  256.       
  257.     private String getHttpRequestBody(PostMethod method) {  
  258.         StringBuilder strBody = new StringBuilder();  
  259.         NameValuePair[] pairs = method.getParameters();  
  260.         for (NameValuePair pair : pairs) {  
  261.             String name = pair.getName();  
  262.             String value = pair.getValue();  
  263.             strBody.append(name + "=" + value + ";");  
  264.         }  
  265.         return strBody.toString();  
  266.     }  
  267.       
  268.     private String getHttpCookie() {  
  269.         StringBuilder strHeader = new StringBuilder();  
  270.         Cookie[] cookies = httpClient.getState().getCookies();  
  271.         for (Cookie cookie : cookies) {  
  272.             String domain = cookie.getDomain();  
  273.             String path = cookie.getPath();  
  274.             String name = cookie.getName();  
  275.             String value = cookie.getValue();  
  276.             Date expired = cookie.getExpiryDate();  
  277.             boolean isSecure = cookie.getSecure();  
  278.             strHeader.append("domain=" + domain + ";");  
  279.             strHeader.append("path=" + path + ";");  
  280.             strHeader.append(name + "=" + value + ";");  
  281.             if (expired != null) {  
  282.                 strHeader.append("expired=" + expired.toGMTString() + ";");  
  283.             }  
  284.             strHeader.append("isSecure=" + isSecure+ "/n");  
  285.         }  
  286.         return strHeader.toString();  
  287.     }  
  288.       
  289.     private String getHttpRequestHeader(HttpMethod method) {  
  290.         StringBuilder strHeader = new StringBuilder();  
  291.         Header[] headers = method.getRequestHeaders();  
  292.         for (Header header : headers) {  
  293.             String name = header.getName();  
  294.             String value = header.getValue();  
  295.             strHeader.append(name + "=" + value + ";");  
  296.         }  
  297.         return strHeader.toString();  
  298.     }  
  299.       
  300.     private String getHttpResponseHeader(HttpMethod method) {  
  301.         StringBuilder strHeader = new StringBuilder();  
  302.         Header[] headers = method.getResponseHeaders();  
  303.         for (Header header : headers) {  
  304.             String name = header.getName();  
  305.             String value = header.getValue();  
  306.             strHeader.append(name + "=" + value + ";");  
  307.         }  
  308.         return strHeader.toString();  
  309.     }  
  310. }  

10.HttpResponse类,http的回复结果类
[java]  view plain copy
  1. package org.gc.contact.http;  
  2. import java.io.ByteArrayInputStream;  
  3. import java.io.InputStream;  
  4. import java.io.UnsupportedEncodingException;  
  5. import org.apache.commons.logging.Log;  
  6. import org.apache.commons.logging.LogFactory;  
  7. import org.gc.contact.util.EncodingUtil;  
  8. public class HttpResponse {  
  9.       
  10.     private static final Log logger = LogFactory.getLog(HttpResponse.class);  
  11.       
  12.     private final String responseUrl;  
  13.     private final byte[] bodyAsBytes;  
  14.     private final String encoding;  
  15.       
  16.     public HttpResponse(String responseUrl, byte[] bodyAsBytes) {  
  17.         this(responseUrl, bodyAsBytes, EncodingUtil.DEFAULT_ENCODING);  
  18.     }  
  19.       
  20.     public HttpResponse(String responseUrl, byte[] bodyAsBytes, String encoding) {  
  21.         this.responseUrl = responseUrl;   
  22.         this.bodyAsBytes = bodyAsBytes;  
  23.         this.encoding = encoding;  
  24.     }  
  25.       
  26.     public String getResponseUrl() {  
  27.         return responseUrl;  
  28.     }  
  29.       
  30.     public String getBodyAsString() {  
  31.         try {  
  32.             if (logger.isDebugEnabled()) {  
  33.                 logger.debug("Convert Encoding=" + encoding);  
  34.             }  
  35.             return new String(bodyAsBytes, encoding);  
  36.         } catch (UnsupportedEncodingException e) {  
  37.             logger.error("Encoding Error[encoding=" + encoding + "]", e);  
  38.             try {  
  39.                 return new String(bodyAsBytes, EncodingUtil.DEFAULT_ENCODING);  
  40.             } catch (UnsupportedEncodingException e1) {  
  41.                 logger.error("Encoding Error[encoding=" + EncodingUtil.DEFAULT_ENCODING + "]", e);  
  42.                 return new String(bodyAsBytes);  
  43.             }  
  44.         }  
  45.     }  
  46.       
  47.     public byte[] getBodyAsBytes() {  
  48.         return bodyAsBytes;  
  49.     }  
  50.       
  51.     public InputStream getBodyAsStream() {  
  52.         return new ByteArrayInputStream(bodyAsBytes);  
  53.     }  
  54.       
  55. }  

11.SSLFactory类,用于https
[java]  view plain copy
  1. package org.gc.contact.http;  
  2. import java.io.IOException;  
  3. import java.net.InetAddress;  
  4. import java.net.InetSocketAddress;  
  5. import java.net.Socket;  
  6. import java.net.SocketAddress;  
  7. import java.net.UnknownHostException;  
  8. import java.security.KeyManagementException;  
  9. import java.security.NoSuchAlgorithmException;  
  10. import java.security.cert.CertificateException;  
  11. import java.security.cert.X509Certificate;  
  12. import javax.net.SocketFactory;  
  13. import javax.net.ssl.SSLContext;  
  14. import javax.net.ssl.TrustManager;  
  15. import javax.net.ssl.X509TrustManager;  
  16. import org.apache.commons.httpclient.ConnectTimeoutException;  
  17. import org.apache.commons.httpclient.params.HttpConnectionParams;  
  18. import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;  
  19. public class SSLFactory implements SecureProtocolSocketFactory {  
  20.       
  21.     static {  
  22.         System.out.println(">>>> SSLFactory Init <<<<");  
  23.     }  
  24.       
  25.     private SSLContext sslcontext = null;  
  26.       
  27.     private SSLContext createSSLContext() {  
  28.         SSLContext sslcontext=null;  
  29.         try {  
  30.             sslcontext = SSLContext.getInstance("SSL");  
  31.             sslcontext.init(nullnew TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());  
  32.         } catch (NoSuchAlgorithmException e) {  
  33.             e.printStackTrace();  
  34.         } catch (KeyManagementException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.         return sslcontext;  
  38.     }  
  39.       
  40.     private SSLContext getSSLContext() {  
  41.         if (this.sslcontext == null) {  
  42.             this.sslcontext = createSSLContext();  
  43.         }  
  44.         return this.sslcontext;  
  45.     }  
  46.       
  47.     public Socket createSocket(Socket socket, String host, int port, boolean autoClose)  
  48.             throws IOException, UnknownHostException {  
  49.         return getSSLContext().getSocketFactory().createSocket(  
  50.                 socket,  
  51.                 host,  
  52.                 port,  
  53.                 autoClose  
  54.             );  
  55.     }  
  56.     public Socket createSocket(String host, int port) throws IOException,  
  57.             UnknownHostException {  
  58.         return getSSLContext().getSocketFactory().createSocket(  
  59.                 host,  
  60.                 port  
  61.             );  
  62.     }  
  63.       
  64.       
  65.     public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)  
  66.             throws IOException, UnknownHostException {  
  67.         return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);  
  68.     }  
  69.     public Socket createSocket(String host, int port, InetAddress localAddress,  
  70.             int localPort, HttpConnectionParams params) throws IOException,  
  71.             UnknownHostException, ConnectTimeoutException {  
  72.         if (params == null) {  
  73.             throw new IllegalArgumentException("Parameters may not be null");  
  74.         }  
  75.         int timeout = params.getConnectionTimeout();  
  76.         SocketFactory socketfactory = getSSLContext().getSocketFactory();  
  77.         if (timeout == 0) {  
  78.             return socketfactory.createSocket(host, port, localAddress, localPort);  
  79.         } else {  
  80.             Socket socket = socketfactory.createSocket();  
  81.             SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);  
  82.             SocketAddress remoteaddr = new InetSocketAddress(host, port);  
  83.             socket.bind(localaddr);  
  84.             socket.connect(remoteaddr, timeout);  
  85.             return socket;  
  86.         }  
  87.     }  
  88.       
  89.     private static class TrustAnyTrustManager implements X509TrustManager {  
  90.          
  91.         public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {  
  92.         }  
  93.      
  94.         public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {  
  95.         }  
  96.      
  97.         public X509Certificate[] getAcceptedIssuers() {  
  98.             return new X509Certificate[]{};  
  99.         }  
  100.     }  
  101. }  

12.SSLUtilities类,用于https
[java]  view plain copy
  1. package org.gc.contact.http;  
  2. import java.security.GeneralSecurityException;  
  3. import java.security.SecureRandom;  
  4. import java.security.cert.X509Certificate;  
  5. import javax.net.ssl.HostnameVerifier;  
  6. import javax.net.ssl.HttpsURLConnection;  
  7. import javax.net.ssl.SSLContext;  
  8. import javax.net.ssl.TrustManager;  
  9. import javax.net.ssl.X509TrustManager;  
  10. /** 
  11.  * This class provide various static methods that relax X509 certificate and 
  12.  * hostname verification while using the SSL over the HTTP protocol. 
  13.  *  
  14.  * @author Francis Labrie 
  15.  */  
  16. public final class SSLUtilities {  
  17.     /** 
  18.      * Hostname verifier for the Sun's deprecated API. 
  19.      *  
  20.      * @deprecated see {@link #_hostnameVerifier}. 
  21.      */  
  22.     private static com.sun.net.ssl.HostnameVerifier __hostnameVerifier;  
  23.     /** 
  24.      * Thrust managers for the Sun's deprecated API. 
  25.      *  
  26.      * @deprecated see {@link #_trustManagers}. 
  27.      */  
  28.     private static com.sun.net.ssl.TrustManager[] __trustManagers;  
  29.     /** 
  30.      * Hostname verifier. 
  31.      */  
  32.     private static HostnameVerifier _hostnameVerifier;  
  33.     /** 
  34.      * Thrust managers. 
  35.      */  
  36.     private static TrustManager[] _trustManagers;  
  37.     /** 
  38.      * Set the default Hostname Verifier to an instance of a fake class that 
  39.      * trust all hostnames. This method uses the old deprecated API from the 
  40.      * com.sun.ssl package. 
  41.      *  
  42.      * @deprecated see {@link #_trustAllHostnames()}. 
  43.      */  
  44.     private static void __trustAllHostnames() {  
  45.         // Create a trust manager that does not validate certificate chains  
  46.         if (__hostnameVerifier == null) {  
  47.             __hostnameVerifier = new _FakeHostnameVerifier();  
  48.         } // if  
  49.         // Install the all-trusting host name verifier  
  50.         com.sun.net.ssl.HttpsURLConnection  
  51.                 .setDefaultHostnameVerifier(__hostnameVerifier);  
  52.     } // __trustAllHttpsCertificates  
  53.     /** 
  54.      * Set the default X509 Trust Manager to an instance of a fake class that 
  55.      * trust all certificates, even the self-signed ones. This method uses the 
  56.      * old deprecated API from the com.sun.ssl package. 
  57.      *  
  58.      * @deprecated see {@link #_trustAllHttpsCertificates()}. 
  59.      */  
  60.     private static void __trustAllHttpsCertificates() {  
  61.         com.sun.net.ssl.SSLContext context;  
  62.         // Create a trust manager that does not validate certificate chains  
  63.         if (__trustManagers == null) {  
  64.             __trustManagers = new com.sun.net.ssl.TrustManager[] { new _FakeX509TrustManager() };  
  65.         } // if  
  66.         // Install the all-trusting trust manager  
  67.         try {  
  68.             context = com.sun.net.ssl.SSLContext.getInstance("SSL");  
  69.             context.init(null, __trustManagers, new SecureRandom());  
  70.         } catch (GeneralSecurityException gse) {  
  71.             throw new IllegalStateException(gse.getMessage());  
  72.         } // catch  
  73.         com.sun.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context  
  74.                 .getSocketFactory());  
  75.     } // __trustAllHttpsCertificates  
  76.     /** 
  77.      * Return true if the protocol handler property java. protocol.handler.pkgs 
  78.      * is set to the Sun's com.sun.net.ssl. internal.www.protocol deprecated 
  79.      * one, false otherwise. 
  80.      *  
  81.      * @return true if the protocol handler property is set to the Sun's 
  82.      *         deprecated one, false otherwise. 
  83.      */  
  84.     private static boolean isDeprecatedSSLProtocol() {  
  85.         return ("com.sun.net.ssl.internal.www.protocol".equals(System  
  86.                 .getProperty("java.protocol.handler.pkgs")));  
  87.     } // isDeprecatedSSLProtocol  
  88.     /** 
  89.      * Set the default Hostname Verifier to an instance of a fake class that 
  90.      * trust all hostnames. 
  91.      */  
  92.     private static void _trustAllHostnames() {  
  93.         // Create a trust manager that does not validate certificate chains  
  94.         if (_hostnameVerifier == null) {  
  95.             _hostnameVerifier = new FakeHostnameVerifier();  
  96.         } // if  
  97.         // Install the all-trusting host name verifier:  
  98.         HttpsURLConnection.setDefaultHostnameVerifier(_hostnameVerifier);  
  99.     } // _trustAllHttpsCertificates  
  100.     /** 
  101.      * Set the default X509 Trust Manager to an instance of a fake class that 
  102.      * trust all certificates, even the self-signed ones. 
  103.      */  
  104.     private static void _trustAllHttpsCertificates() {  
  105.         SSLContext context;  
  106.         // Create a trust manager that does not validate certificate chains  
  107.         if (_trustManagers == null) {  
  108.             _trustManagers = new TrustManager[] { new FakeX509TrustManager() };  
  109.         } // if  
  110.         // Install the all-trusting trust manager:  
  111.         try {  
  112.             context = SSLContext.getInstance("SSL");  
  113.             context.init(null, _trustManagers, new SecureRandom());  
  114.         } catch (GeneralSecurityException gse) {  
  115.             throw new IllegalStateException(gse.getMessage());  
  116.         } // catch  
  117.         HttpsURLConnection.setDefaultSSLSocketFactory(context  
  118.                 .getSocketFactory());  
  119.     } // _trustAllHttpsCertificates  
  120.     /** 
  121.      * Set the default Hostname Verifier to an instance of a fake class that 
  122.      * trust all hostnames. 
  123.      */  
  124.     public static void trustAllHostnames() {  
  125.         // Is the deprecated protocol setted?  
  126.         if (isDeprecatedSSLProtocol()) {  
  127.             __trustAllHostnames();  
  128.         } else {  
  129.             _trustAllHostnames();  
  130.         } // else  
  131.     } // trustAllHostnames  
  132.     /** 
  133.      * Set the default X509 Trust Manager to an instance of a fake class that 
  134.      * trust all certificates, even the self-signed ones. 
  135.      */  
  136.     public static void trustAllHttpsCertificates() {  
  137.         // Is the deprecated protocol setted?  
  138.         if (isDeprecatedSSLProtocol()) {  
  139.             __trustAllHttpsCertificates();  
  140.         } else {  
  141.             _trustAllHttpsCertificates();  
  142.         } // else  
  143.     } // trustAllHttpsCertificates  
  144.     /** 
  145.      * This class implements a fake hostname verificator, trusting any host 
  146.      * name. This class uses the old deprecated API from the com.sun. ssl 
  147.      * package. 
  148.      *  
  149.      * @author Francis Labrie 
  150.      *  
  151.      * @deprecated see {@link SSLUtilities.FakeHostnameVerifier}. 
  152.      */  
  153.     public static class _FakeHostnameVerifier implements  
  154.             com.sun.net.ssl.HostnameVerifier {  
  155.         /** 
  156.          * Always return true, indicating that the host name is an acceptable 
  157.          * match with the server's authentication scheme. 
  158.          *  
  159.          * @param hostname 
  160.          *            the host name. 
  161.          * @param session 
  162.          *            the SSL session used on the connection to host. 
  163.          * @return the true boolean value indicating the host name is trusted. 
  164.          */  
  165.         public boolean verify(String hostname, String session) {  
  166.             return (true);  
  167.         } // verify  
  168.     } // _FakeHostnameVerifier  
  169.     /** 
  170.      * This class allow any X509 certificates to be used to authenticate the 
  171.      * remote side of a secure socket, including self-signed certificates. This 
  172.      * class uses the old deprecated API from the com.sun.ssl package. 
  173.      *  
  174.      * @author Francis Labrie 
  175.      *  
  176.      * @deprecated see {@link SSLUtilities.FakeX509TrustManager}. 
  177.      */  
  178.     public static class _FakeX509TrustManager implements  
  179.             com.sun.net.ssl.X509TrustManager {  
  180.         /** 
  181.          * Empty array of certificate authority certificates. 
  182.          */  
  183.         private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};  
  184.         /** 
  185.          * Always return true, trusting for client SSL chain peer certificate 
  186.          * chain. 
  187.          *  
  188.          * @param chain 
  189.          *            the peer certificate chain. 
  190.          * @return the true boolean value indicating the chain is trusted. 
  191.          */  
  192.         public boolean isClientTrusted(X509Certificate[] chain) {  
  193.             return (true);  
  194.         } // checkClientTrusted  
  195.         /** 
  196.          * Always return true, trusting for server SSL chain peer certificate 
  197.          * chain. 
  198.          *  
  199.          * @param chain 
  200.          *            the peer certificate chain. 
  201.          * @return the true boolean value indicating the chain is trusted. 
  202.          */  
  203.         public boolean isServerTrusted(X509Certificate[] chain) {  
  204.             return (true);  
  205.         } // checkServerTrusted  
  206.         /** 
  207.          * Return an empty array of certificate authority certificates which are 
  208.          * trusted for authenticating peers. 
  209.          *  
  210.          * @return a empty array of issuer certificates. 
  211.          */  
  212.         public X509Certificate[] getAcceptedIssuers() {  
  213.             return (_AcceptedIssuers);  
  214.         } // getAcceptedIssuers  
  215.     } // _FakeX509TrustManager  
  216.     /** 
  217.      * This class implements a fake hostname verificator, trusting any host 
  218.      * name. 
  219.      *  
  220.      * @author Francis Labrie 
  221.      */  
  222.     public static class FakeHostnameVerifier implements HostnameVerifier {  
  223.         /** 
  224.          * Always return true, indicating that the host name is an acceptable 
  225.          * match with the server's authentication scheme. 
  226.          *  
  227.          * @param hostname 
  228.          *            the host name. 
  229.          * @param session 
  230.          *            the SSL session used on the connection to host. 
  231.          * @return the true boolean value indicating the host name is trusted. 
  232.          */  
  233.         public boolean verify(String hostname, javax.net.ssl.SSLSession session) {  
  234.             return (true);  
  235.         } // verify  
  236.     } // FakeHostnameVerifier  
  237.     /** 
  238.      * This class allow any X509 certificates to be used to authenticate the 
  239.      * remote side of a secure socket, including self-signed certificates. 
  240.      *  
  241.      * @author Francis Labrie 
  242.      */  
  243.     public static class FakeX509TrustManager implements X509TrustManager {  
  244.         /** 
  245.          * Empty array of certificate authority certificates. 
  246.          */  
  247.         private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};  
  248.         /** 
  249.          * Always trust for client SSL chain peer certificate chain with any 
  250.          * authType authentication types. 
  251.          *  
  252.          * @param chain 
  253.          *            the peer certificate chain. 
  254.          * @param authType 
  255.          *            the authentication type based on the client certificate. 
  256.          */  
  257.         public void checkClientTrusted(X509Certificate[] chain, String authType) {  
  258.         } // checkClientTrusted  
  259.         /** 
  260.          * Always trust for server SSL chain peer certificate chain with any 
  261.          * authType exchange algorithm types. 
  262.          *  
  263.          * @param chain 
  264.          *            the peer certificate chain. 
  265.          * @param authType 
  266.          *            the key exchange algorithm used. 
  267.          */  
  268.         public void checkServerTrusted(X509Certificate[] chain, String authType) {  
  269.         } // checkServerTrusted  
  270.         /** 
  271.          * Return an empty array of certificate authority certificates which are 
  272.          * trusted for authenticating peers. 
  273.          *  
  274.          * @return a empty array of issuer certificates. 
  275.          */  
  276.         public X509Certificate[] getAcceptedIssuers() {  
  277.             return (_AcceptedIssuers);  
  278.         } // getAcceptedIssuers  
  279.     } // FakeX509TrustManager  
  280. // SSLUtilities  


下面就是取得联系人的过程了 
13.IContactFetcher接口
[java]  view plain copy
  1. package org.gc.contact.fetcher;  
  2. import java.util.List;  
  3. import org.gc.contact.exception.HttpBrowserException;  
  4. import org.gc.contact.exception.HttpServerException;  
  5. import org.gc.contact.exception.ProtocolException;  
  6. import org.gc.contact.model.ContactInfo;  
  7. public interface IContactFetcher {  
  8.       
  9.     void doLogin() throws HttpBrowserException, HttpServerException, ProtocolException;  
  10.     List<ContactInfo> fetchContactInfo() throws HttpBrowserException, HttpServerException, ProtocolException;  
  11.       
  12. }  

14.AbstractContactFetcher类
[java]  view plain copy
  1. package org.gc.contact.fetcher;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import java.util.regex.Matcher;  
  5. import java.util.regex.Pattern;  
  6. import org.apache.commons.logging.Log;  
  7. import org.apache.commons.logging.LogFactory;  
  8. import org.gc.contact.exception.ExceptionMessage;  
  9. import org.gc.contact.exception.ProtocolException;  
  10. import org.gc.contact.http.HttpBrowser;  
  11. import org.gc.contact.util.RegexpUtil;  
  12. public abstract class AbstractContactFetcher implements IContactFetcher {  
  13.       
  14.     protected final Log logger = LogFactory.getLog(getClass());  
  15.     protected final HttpBrowser browser;  
  16.       
  17.     protected String username;  
  18.     protected String password;  
  19.       
  20.     public AbstractContactFetcher(String username, String password) {  
  21.         this.username = username;  
  22.         this.password = password;  
  23.         browser = new HttpBrowser();  
  24.     }  
  25.       
  26.     protected String getJSRedirection(String htmlContent) throws ProtocolException {  
  27.         Pattern p = RegexpUtil.getJSRedirectionRegexp();  
  28.         Matcher matcher = p.matcher(htmlContent);  
  29.         if (!matcher.find()) {  
  30.             String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=JSRedirection,Regexp=" + p.pattern() + "]";  
  31.             logger.error(msg);  
  32.             throw new ProtocolException(msg);  
  33.         }  
  34.         if (logger.isDebugEnabled()) {  
  35.             logger.debug("Redirection URL = " + matcher.group(1));  
  36.         }  
  37.         return matcher.group(1);  
  38.     }  
  39.       
  40.     protected String getFormInputValue(String name, String htmlContent) throws ProtocolException {  
  41.         Pattern p = RegexpUtil.getFormInputValueRegexp();  
  42.         int index = htmlContent.indexOf(name);  
  43.         int prePos = index - 400 > 0 ? index - 400 : 0;  
  44.         int postPos = index + 400 < htmlContent.length() ? index + 400 : htmlContent.length();  
  45.         int start = htmlContent.substring(prePos, index).lastIndexOf("<input") + prePos;  
  46.         int end = htmlContent.substring(index, postPos).indexOf(">") + index;  
  47.         String htmlInput = htmlContent.substring(start, end + 1);  
  48.         if (logger.isDebugEnabled()) {  
  49.             logger.debug("HtmlInput=" + htmlInput);  
  50.         }  
  51.         Matcher matcher = p.matcher(htmlInput);  
  52.         if (!matcher.find()) {  
  53.             String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=FormInputValue,Regexp=" + p.pattern() + "]";  
  54.             logger.error(msg);  
  55.             throw new ProtocolException(msg);  
  56.         }  
  57.         if (logger.isDebugEnabled()) {  
  58.             logger.debug("Name = " + name + ";value = " + matcher.group(1));  
  59.         }  
  60.         return matcher.group(1);  
  61.     }  
  62.       
  63.     protected String getHost(String url) throws ProtocolException {  
  64.         Pattern p = RegexpUtil.getHostFromURIRegexp();  
  65.         Matcher matcher = p.matcher(url);  
  66.         if (!matcher.find()) {  
  67.             String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=Host,Regexp=" + p.pattern() + "]";  
  68.             logger.error(msg);  
  69.             throw new ProtocolException(msg);  
  70.         }  
  71.         if (logger.isDebugEnabled()) {  
  72.             logger.debug("Host = " + matcher.group(1));  
  73.         }  
  74.         return matcher.group(1);  
  75.     }  
  76.       
  77.     protected List<String> getValue(Pattern p, String htmlContent) throws ProtocolException {  
  78.         List<String> data = new ArrayList<String>();  
  79.         Matcher matcher = p.matcher(htmlContent);  
  80.         while (matcher.find()) {  
  81.             try {  
  82.                 data.add(matcher.group(1));  
  83.                 if (logger.isDebugEnabled()) {  
  84.                     logger.debug("Matched String=" + matcher.group(1));  
  85.                 }  
  86.             } catch (Exception e) {  
  87.                 String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=getValue,Regexp=" + p.pattern() + "]";  
  88.                 logger.error(msg);  
  89.                 throw new ProtocolException(msg);  
  90.             }  
  91.         }  
  92.         if (data.size() == 0) {  
  93.             String msg = ExceptionMessage.PROTOCOL_MSG + "[Type=getValue,Regexp=" + p.pattern() + "]";  
  94.             logger.warn(msg);  
  95.         }  
  96.         return data;  
  97.     }  
  98. }  

15.取163的联系人
[java]  view plain copy
  1. package org.gc.contact.fetcher;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import java.util.regex.Pattern;  
  5. import org.apache.commons.httpclient.NameValuePair;  
  6. import org.gc.contact.exception.HttpBrowserException;  
  7. import org.gc.contact.exception.HttpServerException;  
  8. import org.gc.contact.exception.ProtocolException;  
  9. import org.gc.contact.http.HttpResponse;  
  10. import org.gc.contact.model.ContactInfo;  
  11. /** 
  12.  * @163.com邮箱取地址本过程 
  13.  * 1. loginUrl 
  14.  * 2. window.location.replace("url") 
  15.  * 3. fetch address list 
  16.  * 
  17.  */  
  18. public class Mail163ContactFetcher extends AbstractContactFetcher {  
  19.       
  20.     private final String loginUrl = "http://reg.163.com/login.jsp?type=1&product=mail163&url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D35";  
  21.       
  22.     /** 
  23.      * fetch url 
  24.      * http://g1a61.mail.163.com/jy3/address/addrlist.jsp?sid=SBmtqsXXQmCXunRFLtXXXfowbRxMPUUX&gid=all 
  25.     */  
  26.     private String fetchUrl;  
  27.       
  28.     private final String REGEXP_USER = "<td class=/"Ibx_Td_addrName/"><a href="/" mce_href="/""addrdetail.jsp//?sid=.+&gid=all/" title=/".+/">(.+)</a></td>";  
  29.     private final String REGEXP_EMAIL = "<td class=/"Ibx_Td_addrEmail/"><a href="/" mce_href="/""addrdetail.jsp//?sid=.+&gid=all/" title=/".+/">(.+)</a></td>";  
  30.     private final Pattern PATTERN_USER = Pattern.compile(REGEXP_USER);  
  31.     private final Pattern PATTERN_EMAIL = Pattern.compile(REGEXP_EMAIL);  
  32.       
  33.     public Mail163ContactFetcher(String username, String password) {  
  34.         super(username, password);  
  35.     }  
  36.     @Override  
  37.     public void doLogin() throws HttpBrowserException, HttpServerException, ProtocolException {  
  38.         NameValuePair[] loginParams = makeLoginParams();  
  39.         HttpResponse redirectPage = browser.doPost(loginUrl, loginParams);  
  40.         String redirectUrl = getJSRedirection(redirectPage.getBodyAsString());  
  41.         HttpResponse mainPage = browser.doGet(redirectUrl);  
  42.         fetchUrl = mainPage.getResponseUrl();  
  43.     }  
  44.     @Override  
  45.     public List<ContactInfo> fetchContactInfo() throws HttpBrowserException, HttpServerException, ProtocolException {  
  46.         List<ContactInfo> contacts = new ArrayList<ContactInfo>();  
  47.         String contactUrl = fetchUrl.replace("main.jsp""address/addrlist.jsp");  
  48.         contactUrl += "&gid=all";  
  49.         HttpResponse contactPage = browser.doGet(contactUrl);  
  50.         String html = contactPage.getBodyAsString();  
  51.         //为了匹配,每个td一行  
  52.         html = html.replace("</td>""</td>/n");  
  53.         List<String> username = getValue(PATTERN_USER, html);  
  54.         List<String> email = getValue(PATTERN_EMAIL, html);  
  55.         for (int i = 0; i < username.size(); i++) {  
  56.             contacts.add(new ContactInfo(username.get(i), email.get(i)));  
  57.         }  
  58.         return contacts;  
  59.     }  
  60.       
  61.     private NameValuePair[] makeLoginParams() {  
  62.         return new NameValuePair[] {  
  63.                 new NameValuePair("username", username),  
  64.                 new NameValuePair("password", password)  
  65.         };  
  66.     }  
  67.       
  68. }  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值