changeset 355:0fe895195132

implement OAuth authentication.
author Yoshiki Yazawa <[email protected]>
date Thu, 26 Aug 2010 15:05:33 +0900
parents ff078879e68e
children e8e51c70793c
files pidgin-twitter.h twitter_api.c twitter_api.h
diffstat 3 files changed, 545 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/pidgin-twitter.h	Tue May 18 00:12:59 2010 +0900
+++ b/pidgin-twitter.h	Thu Aug 26 15:05:33 2010 +0900
@@ -132,11 +132,15 @@
 #define OPT_ICON_DIR            OPT_PIDGINTWITTER "/icon_dir"
 #define OPT_API_BASE_POST       OPT_PIDGINTWITTER "/api_base_post"
 #define OPT_SCREEN_NAME_TWITTER OPT_PIDGINTWITTER "/screen_name_twitter"
+#define OPT_PASSWORD_TWITTER    OPT_PIDGINTWITTER "/password_twitter"
+
+#define OPT_AKEY_TWITTER        OPT_PIDGINTWITTER "/akey_twitter"
+#define OPT_ASEC_TWITTER        OPT_PIDGINTWITTER "/asec_twitter"
+
 #define OPT_SCREEN_NAME_WASSR   OPT_PIDGINTWITTER "/screen_name_wassr"
 #define OPT_SCREEN_NAME_IDENTICA OPT_PIDGINTWITTER "/screen_name_identica"
 #define OPT_SCREEN_NAME_JISKO   OPT_PIDGINTWITTER "/screen_name_jisko"
 #define OPT_SCREEN_NAME_FFEED   OPT_PIDGINTWITTER "/screen_name_ffeed"
-#define OPT_PASSWORD_TWITTER    OPT_PIDGINTWITTER "/password_twitter"
 #define OPT_SHOW_ICON           OPT_PIDGINTWITTER "/show_icon"
 #define OPT_ICON_SIZE           OPT_PIDGINTWITTER "/icon_size"
 #define OPT_UPDATE_ICON         OPT_PIDGINTWITTER "/update_icon"
@@ -204,41 +208,6 @@
 /* pttag=msgid:in_reply_to_status_id:in_reply_to_screen_name */
 #define P_PTTAG_TWITTER     " pttag=([0-9]+):([0-9]+):([-A-Za-z0-9_]*)$"
 
-/* twitter API specific macros */
-#define TWITTER_BASE_URL "http://twitter.com"
-#define TWITTER_API_BASE_URL "http://api.twitter.com"
-#if 0
-#define TWITTER_STATUS_GET "GET /statuses/friends_timeline.xml?count=%d HTTP/1.1\r\n" \
-    "Host: twitter.com\r\n"                                          \
-    "User-Agent: pidgin-twitter\r\n"                                 \
-    "Authorization: Basic %s\r\n"
-#endif
-#define TWITTER_STATUS_GET "GET /1/statuses/home_timeline.xml?count=%d HTTP/1.1\r\n" \
-    "Host: api.twitter.com\r\n"                                          \
-    "User-Agent: pidgin-twitter\r\n"                                 \
-    "Authorization: Basic %s\r\n"
-#define TWITTER_STATUS_POST "POST /statuses/update.xml HTTP/1.1\r\n" \
-    "Host: twitter.com\r\n"                                          \
-    "User-Agent: pidgin-twitter\r\n"                                 \
-    "Authorization: Basic %s\r\n"                                    \
-    "Content-Length: %d\r\n"
-#define TWITTER_FAV_POST "POST /favorites/create/%llu.xml HTTP/1.1\r\n" \
-    "Host: twitter.com\r\n"                                          \
-    "User-Agent: pidgin-twitter\r\n"                                 \
-    "Authorization: Basic %s\r\n"
-#define TWITTER_RETWEET_POST "POST /1/statuses/retweet/%llu.xml HTTP/1.1\r\n" \
-    "Host: api.twitter.com\r\n"                                          \
-    "User-Agent: pidgin-twitter\r\n"                                 \
-    "Authorization: Basic %s\r\n"
-
-#define TWITTER_STATUS_FORMAT "status=%s&source=pidgintwitter"
-#define TWITTER_REPLY_FORMAT  "status=%s&in_reply_to_status_id=%llu&source=pidgintwitter"
-
-#define TWITTER_DEFAULT_INTERVAL (60)
-#define TWITTER_OLD_DEFAULT_ICON_URL "http://static.twitter.com/images/default_profile_bigger.png"
-#define TWITTER_DEFAULT_ICON_URL "http://s.twimg.com/images/default_profile_3_bigger.png"
-
-#define TWITTER_DEFAULT_RETRIEVE_COUNT (20)
 
 /* wassr specific macros */
 #define WASSR_POST_LEN (255)
--- a/twitter_api.c	Tue May 18 00:12:59 2010 +0900
+++ b/twitter_api.c	Thu Aug 26 15:05:33 2010 +0900
@@ -13,6 +13,30 @@
 static void get_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
 static void post_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
 
+/* oauth */
+#define TYPE_GET  0
+#define TYPE_POST 1
+
+typedef struct oauth_request {
+    char *url;
+    char *c_key;
+    char *c_sec;
+    char *a_key;
+    char *a_sec;
+    char *verifier;
+    char *status;
+    PurpleConversation *conv;
+    guint64 msgid;
+    int count;
+    int type;
+    gboolean notoken;
+} oauth_request_t;
+
+char *make_oauth_get(oauth_request_t *oauth_req);
+char *make_oauth_post(oauth_request_t *auth_req);
+static void oauth_setup_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
+
+
 #ifdef _WIN32
 extern gboolean blink_state;
 extern gboolean blink_modified;
@@ -21,6 +45,345 @@
 extern guint64 reply_to_msgid;
 extern PurpleAccount *account_for_twitter;
 
+char *request_token_url = "http://twitter.com/oauth/request_token";
+char *access_token_url  = "http://twitter.com/oauth/access_token";
+char *authorize_url     = "http://twitter.com/oauth/authorize";
+
+char *c_key = "wrD3WGIh2P31d3fIjRkfcw";
+char *c_sec = "ZEhViGY8P5IPjwgV8EVOkdjHhShRAZ9yhlYw0ZDXU";
+char *SAMPLE_NONCE = "0123456789abcdefghijk";
+
+void
+oauth_access_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+                        const gchar *url_text, size_t len,
+                        const gchar *error_message)
+{
+    char *f = NULL, *e = NULL;
+    oauth_request_t *oauth_req = (oauth_request_t *)user_data;
+
+    /* separate key and secret */
+    f = strstr(url_text, "oauth_token=");
+    if(!f)
+        return;
+    e = strstr(f, "&");
+    if(!e)
+        return;
+    g_free(oauth_req->a_key);
+    oauth_req->a_key = g_strndup(f+12, e-f-12);
+
+    f = strstr(e+1, "oauth_token_secret=");
+    if(!f)
+        return;
+    e = strstr(f, "&");
+    if(!e)
+        return;
+    g_free(oauth_req->a_sec);
+    oauth_req->a_sec = g_strndup(f+19, e-f-19);
+
+    /* write a_key and a_sec to prefs */
+    purple_prefs_set_string(OPT_AKEY_TWITTER, oauth_req->a_key);
+    purple_prefs_set_string(OPT_ASEC_TWITTER, oauth_req->a_sec);
+
+    /* invoke fetch xxx */
+    g_usleep(3*1000000); /* wait for server configuration */
+    get_status_with_api((gpointer)oauth_req->conv);
+
+    /* all done */
+    g_free(oauth_req->url);
+    g_free(oauth_req->c_key);
+    g_free(oauth_req->c_sec);
+    g_free(oauth_req->a_key);
+    g_free(oauth_req->a_sec);
+    g_free(oauth_req);
+}
+
+
+void
+pin_dialog_ok_cb(gpointer data, char *pin)
+{
+    char *oauth = NULL;
+    char *request = NULL;
+    oauth_request_t *oauth_req = (oauth_request_t *)data;
+/*
+request URL:http://twitter.com/oauth/access_token?
+    oauth_consumer_key=wrD3WGIh2P31d3fIjRkfcw&
+    oauth_nonce=QUzhkt0AO3tjNrR&
+    oauth_signature_method=HMAC-SHA1&
+    oauth_timestamp=1282450223&
+
+    oauth_token=VL126k8KRNXid7Q7ZHYHh05PuASunVaPzyzrozf14&
+    oauth_verifier=9772286&
+    oauth_version=1.0&
+    oauth_signature=OyBsxqWdsvLkV4LBnN8tMBnImVY%3D
+*/
+
+    /* access token*/
+    g_free(oauth_req->url);
+    oauth_req->url = g_strdup(access_token_url);
+    oauth_req->verifier = g_strdup(pin);
+    oauth_req->type = TYPE_GET;
+    oauth_req->notoken = FALSE;
+
+    twitter_debug("a_key=%s\n", oauth_req->a_key);
+
+    oauth = make_oauth_get(oauth_req);
+
+    request = g_strdup_printf("%s?%s", access_token_url, oauth);
+    twitter_debug("request=%s\n", request);
+
+    purple_util_fetch_url_request(request, TRUE,
+                                  NULL, TRUE, NULL, TRUE,
+                                  oauth_access_cb, data);
+    g_free(oauth);
+    g_free(request);
+}
+
+
+static void
+oauth_setup_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+                        const gchar *url_text, size_t len,
+                        const gchar *error_message)
+{
+    char *f = NULL, *e = NULL;
+    oauth_request_t *oauth_req = (oauth_request_t *)user_data;
+    PurpleConversation *conv = oauth_req->conv;
+    PurpleAccount *account = purple_conversation_get_account(conv);
+    PurpleConnection *conn = purple_conversation_get_gc(conv);
+
+    g_return_if_fail(url_text != NULL);
+    twitter_debug("len=%d\n", (int)len);
+    twitter_debug("url_text=%s\n", url_text);
+
+/*
+    HTTP-reply:
+    oauth_token=zKILrNcVPGRtUE6Rlh1KU6upYNJWW51mzt7btZx5Ac&
+    oauth_token_secret=EMD3u1piAKPsQnq44Its9f8WmIReYnUFcJIgd3niu4&
+    oauth_callback_confirmed=true
+*/
+    /* separate key and secret */
+    f = strstr(url_text, "oauth_token=");
+    if(!f)
+        return;
+    e = strstr(f, "&");
+    if(!e)
+        return;
+    g_free(oauth_req->a_key);
+    oauth_req->a_key = g_strndup(f+12, e-f-12);
+
+    f = strstr(e+1, "oauth_token_secret=");
+    if(!f)
+        return;
+    e = strstr(f, "&");
+    if(!e)
+        return;
+    g_free(oauth_req->a_sec);
+    oauth_req->a_sec = g_strndup(f+19, e-f-19);
+
+    /* redirect twitter's authorization url */
+    char *uri = g_strdup_printf("%s?oauth_token=%s", authorize_url, oauth_req->a_key);
+    twitter_debug("auth uri=%s\n", uri);
+    purple_notify_uri(conn, uri);
+
+    /* show dialog to wait PIN number*/
+    purple_request_input(conn,
+                         "PIN",
+                         "Enter PIN",
+                         "Press allow button in the browser, then enter the PIN  to complete process.",
+                         "",
+                         FALSE,
+                         FALSE,
+                         NULL,
+                         "OK", G_CALLBACK(pin_dialog_ok_cb),
+                         "Cancel", NULL,
+                         account,
+                         NULL,
+                         NULL,
+                         user_data);
+
+    g_free(uri);
+}
+
+void
+oauth_setup(gpointer data)
+{
+    char *oauth = NULL;
+    char *request = NULL;
+    oauth_request_t *oauth_req = g_new0(oauth_request_t, 1);
+/*
+http://twitter.com/oauth/request_token?
+    oauth_consumer_key=wrD3WGIh2P31d3fIjRkfcw&
+    oauth_nonce=HCUxu1D3qN4Nklr9QVAymve40PtJyU&
+    oauth_signature_method=HMAC-SHA1&
+    oauth_timestamp=1282446611&oauth_version=1.0&
+    oauth_signature=A%2BZIiUVsQv5ZR8u%2F2oLmUFX1eHE%3D
+*/
+
+    oauth_req->url = strdup(request_token_url);
+    oauth_req->c_key = strdup(c_key);
+    oauth_req->c_sec = strdup(c_sec);
+    oauth_req->a_key = NULL;
+    oauth_req->a_sec = NULL;
+    oauth_req->type = TYPE_GET;
+    oauth_req->notoken = TRUE;
+    oauth_req->conv = (PurpleConversation *)data;
+
+    /* request token*/
+    oauth = make_oauth_get(oauth_req);
+    request = g_strdup_printf("%s?%s", request_token_url, oauth);
+
+    twitter_debug("request=%s\n", request);
+
+    purple_util_fetch_url_request(request, TRUE,
+                                  NULL, TRUE, NULL, TRUE,
+                                  oauth_setup_cb, oauth_req);
+}
+
+
+char *
+hmac_sha1(char *text, char *key) {
+	PurpleCipherContext *context = NULL;
+	size_t len;
+	guchar digest[255];
+	char *signature = NULL;
+
+    twitter_debug("text=%s\n", text);
+    twitter_debug("key=%s\n", key);
+
+    context = purple_cipher_context_new_by_name("hmac", NULL);
+    if(!context)
+        return NULL;
+
+	purple_cipher_context_set_option(context, "hash", "sha1");
+	purple_cipher_context_set_key(context, (guchar *)key);
+	purple_cipher_context_append(context, (guchar *)text, strlen(text));
+
+	if(purple_cipher_context_digest(context, sizeof(digest), digest, &len)) {
+		signature = purple_base64_encode(digest, len);
+        twitter_debug("hmac1 signature=%s\n", signature);
+	}
+    else {
+		twitter_debug("digest signature failed\n");
+	}
+
+	purple_cipher_context_destroy(context);
+
+	return signature;
+}
+
+char *
+make_oauth_get(oauth_request_t *req)
+{
+    gchar *tmp = NULL;
+    char *signature = NULL;
+    time_t current_time = time(NULL);
+    char *params = NULL;
+    char *oauth;
+    char *count_str = NULL;
+    char *token_str = NULL;
+    char *verifier_str = NULL;
+
+    if(req->count)
+        count_str = g_strdup_printf("count=%d&", req->count);
+    else
+        count_str = g_strdup("");
+
+    if(req->notoken) {
+        twitter_debug("notoken\n");
+        token_str = g_strdup("");
+    }
+    else
+        token_str = g_strdup_printf("oauth_token=%s&", req->a_key?req->a_key:req->c_key);
+
+    if(req->verifier)
+        verifier_str = g_strdup_printf("oauth_verifier=%s&", req->verifier);
+    else
+        verifier_str = g_strdup("");
+
+    params = g_strdup_printf("%soauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%d&%s%soauth_version=1.0",
+                             count_str,
+                             req->c_key,
+                             SAMPLE_NONCE,
+                             (int)current_time,
+                             token_str,
+                             verifier_str);
+
+    g_free(count_str);
+    g_free(token_str);
+    g_free(verifier_str);
+
+    const char *url_encoded = g_uri_escape_string(req->url, "", FALSE);
+    const char *params_encoded = g_uri_escape_string(params, "", FALSE);
+
+    tmp = g_strdup_printf("GET&%s&%s", url_encoded, params_encoded);
+    char *key = g_strdup_printf("%s&%s", req->c_sec, req->a_sec?req->a_sec:"");
+    signature = hmac_sha1(tmp, key);
+    g_free(key);
+
+    const char *signature_encoded = g_uri_escape_string(signature, "", FALSE);
+
+    oauth = g_strdup_printf("%s&oauth_signature=%s", params, signature_encoded);
+
+    g_free(tmp);
+    g_free(signature);
+    g_free(params);
+
+    twitter_debug("oauth_block=%s\n", oauth);
+    return oauth;
+}
+
+char *
+make_oauth_post(oauth_request_t *req)
+{
+    gchar *tmp = NULL;
+    char *signature = NULL;
+    time_t current_time = time(NULL);
+    char *params = NULL;
+    char *oauth;
+    char *status_str = NULL;
+    char *msgid_str = NULL;
+
+    if(req->status)
+        status_str = g_strdup_printf("&status=%s", req->status);
+    else
+        status_str = g_strdup("");
+
+    if(req->msgid)
+        msgid_str = g_strdup_printf("in_reply_to_status_id=%llu&",
+                                    (long long unsigned int)req->msgid);
+    else
+        msgid_str = g_strdup("");
+
+    params = g_strdup_printf("%soauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%d&oauth_token=%s&oauth_version=1.0%s",
+                             msgid_str,
+                             req->c_key,
+                             SAMPLE_NONCE,
+                             (int)current_time,
+                             req->a_key,
+                             status_str);
+
+    g_free(status_str);
+    g_free(msgid_str);
+
+    const char *url_encoded = g_uri_escape_string(req->url, "", FALSE);
+    const char *params_encoded = g_uri_escape_string(params, "", FALSE);
+
+    tmp = g_strdup_printf("POST&%s&%s", url_encoded, params_encoded);
+    char *key = g_strdup_printf("%s&%s", req->c_sec, req->a_sec);
+    signature = hmac_sha1(tmp, key);
+    g_free(key);
+
+    const char *signature_encoded = g_uri_escape_string(signature, "", FALSE);
+
+    oauth = g_strdup_printf("%s&oauth_signature=%s", params, signature_encoded);
+
+    g_free(tmp);
+    g_free(signature);
+    g_free(params);
+
+    twitter_debug("oauth_block=%s\n", oauth);
+    return oauth;
+}
+
 /**************************/
 /* API base get functions */
 /**************************/
@@ -317,8 +680,23 @@
 
     /* fetch friends time line */
     char *request, *header;
-    char *basic_auth, *basic_auth_encoded;
     gint count = purple_prefs_get_int(OPT_RETRIEVE_COUNT);
+    char *oauth;
+    const char *a_key = NULL;
+    const char *a_sec = NULL;
+    static gboolean setup = FALSE;
+    oauth_request_t oauth_req;
+
+    a_key = purple_prefs_get_string(OPT_AKEY_TWITTER);
+    a_sec = purple_prefs_get_string(OPT_ASEC_TWITTER);
+
+    if(!a_key || !a_sec) {
+        if(!setup) {
+            oauth_setup(data);
+            setup = TRUE;
+        }
+        return TRUE;
+    }
 
     if(count < TWITTER_DEFAULT_RETRIEVE_COUNT)
         count = TWITTER_DEFAULT_RETRIEVE_COUNT;
@@ -327,35 +705,37 @@
     if(!purple_prefs_get_bool(OPT_API_BASE_POST))
         return TRUE;
 
-    const char *screen_name =
-        purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER);
-    const char *password =
-        purple_prefs_get_string(OPT_PASSWORD_TWITTER);
+    /* oauth */
+    char *url0 = g_strdup_printf(TWITTER_API_BASE_URL "/1/statuses/home_timeline.xml");
+    oauth_req.url = url0;
+    oauth_req.c_key = c_key;
+    oauth_req.c_sec = c_sec;
+    oauth_req.a_key = (char *)a_key;
+    oauth_req.a_sec = (char *)a_sec;
+    oauth_req.verifier = NULL;
+    oauth_req.status = NULL;
+    oauth_req.type = TYPE_GET;
+    oauth_req.count = count;
+    oauth_req.msgid = 0;
+    oauth_req.notoken = FALSE;
 
-    if (!screen_name || !password || !screen_name[0] || !password[0]) {
-        twitter_debug("screen_name or password is empty\n");
-        return TRUE;
-    }
-
-    /* auth */
-    basic_auth = g_strdup_printf("%s:%s", screen_name, password);
-    basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth,
-                                              strlen(basic_auth));
-    g_free(basic_auth);
+    oauth = make_oauth_get(&oauth_req);
 
     /* header */
-    header = g_strdup_printf(TWITTER_STATUS_GET, count, basic_auth_encoded);
+    header = g_strdup_printf(TWITTER_STATUS_GET, oauth);
     request = g_strconcat(header, "\r\n", NULL);
 
+    twitter_debug("request=%s\n", request);
+
     /* invoke fetch */
     purple_util_fetch_url_request(TWITTER_API_BASE_URL, FALSE,
                                   NULL, TRUE, request, TRUE,
                                   get_status_with_api_cb, data);
 
     g_free(header);
-    g_free(basic_auth_encoded);
     g_free(request);
-
+    g_free(oauth);
+    g_free(url0);
     return TRUE;
 }
 
@@ -514,21 +894,23 @@
 void
 post_status_with_api(PurpleAccount *account, char **buffer)
 {
-    char *request, *status, *header;
-    const char *url_encoded = g_uri_escape_string(*buffer, "!$'()*,;=:@/?#[]", FALSE);
-    char *basic_auth, *basic_auth_encoded;
-
+    char *request, *header;
+    const char *url_encoded = g_uri_escape_string(*buffer, "", FALSE);
+    PurpleConversation *conv;
+    char *oauth;
     twitter_message_t *tm;
+    const char *a_key = NULL;
+    const char *a_sec = NULL;
+    oauth_request_t oauth_req;
 
-    const char *screen_name =
-        purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER);
-    const char *password = purple_prefs_get_string(OPT_PASSWORD_TWITTER);
+    conv = purple_find_conversation_with_account(
+        PURPLE_CONV_TYPE_ANY, "[email protected]",
+        account_for_twitter); /* xxx */
 
-    twitter_debug("tm.account: %s\n",
-                  purple_account_get_username(account));
+    a_key = purple_prefs_get_string(OPT_AKEY_TWITTER);
+    a_sec = purple_prefs_get_string(OPT_ASEC_TWITTER);
 
-    if (!screen_name || !password || !screen_name[0] || !password[0]) {
-        twitter_debug("screen_name or password is empty\n");
+    if(!a_key || !a_sec) {
         return;
     }
 
@@ -537,32 +919,37 @@
     tm->status = g_strdup(*buffer);
     tm->time = time(NULL);
 
-    basic_auth = g_strdup_printf("%s:%s", screen_name, password);
-    basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth,
-                                              strlen(basic_auth));
-    g_free(basic_auth);
+    /* oauth */
+    char *url0 = g_strdup_printf(TWITTER_API_BASE_URL "/1/statuses/update.xml");
 
-    if(reply_to_msgid > 0) {
-        status = g_strdup_printf(TWITTER_REPLY_FORMAT, url_encoded,
-                                 (long long unsigned int)reply_to_msgid);
-        header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded,
-                                 (int)strlen(status));
-        reply_to_msgid = 0;
-    }
-    else {
-        status = g_strdup_printf(TWITTER_STATUS_FORMAT, url_encoded);
-        header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded,
-                                 (int)strlen(status));
-    }
+    oauth_req.url = url0;
+    oauth_req.c_key = c_key;
+    oauth_req.c_sec = c_sec;
+    oauth_req.a_key = (char *)purple_prefs_get_string(OPT_AKEY_TWITTER);
+    oauth_req.a_sec = (char *)purple_prefs_get_string(OPT_ASEC_TWITTER);
+    oauth_req.verifier = NULL;
+    oauth_req.status = (char *)url_encoded;
+    oauth_req.type = TYPE_POST;
+    oauth_req.count = 0;
+    oauth_req.msgid = reply_to_msgid;
+    oauth_req.notoken = FALSE;
 
-    request = g_strconcat(header, "\r\n", status, "\r\n", NULL);
+    oauth = make_oauth_post(&oauth_req);
+
+    reply_to_msgid = 0;
+
+
+    header = g_strdup_printf(TWITTER_STATUS_POST,
+                             (int)strlen(oauth));
+
+    request = g_strconcat(header, "\r\n", oauth, "\r\n", NULL);
+    twitter_debug("request=%s\n", request);
     purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE,
                                   NULL, TRUE, request, TRUE,
                                   post_status_with_api_cb, tm);
 
     g_free(header);
-    g_free(basic_auth_encoded);
-    g_free(status);
+    g_free(oauth);
     g_free(request);
 
 }
@@ -579,40 +966,54 @@
 fav_with_api(guint64 id)
 {
     char *header, *request;
-    char *basic_auth, *basic_auth_encoded;
+    char *oauth;
+    const char *a_key = NULL;
+    const char *a_sec = NULL;
+    PurpleConversation *conv;
+    oauth_request_t oauth_req;
 
-    const char *screen_name =
-        purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER);
-    const char *password = purple_prefs_get_string(OPT_PASSWORD_TWITTER);
+    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+                                                 "[email protected]",
+                                                 account_for_twitter); /* xxx */
+
 
-    if(id == 0) {
-        twitter_debug("invalid message id\n");
+    a_key = purple_prefs_get_string(OPT_AKEY_TWITTER);
+    a_sec = purple_prefs_get_string(OPT_ASEC_TWITTER);
+
+    if(!a_key || !a_sec) {
         return;
     }
 
-    if (!screen_name || !password || !screen_name[0] || !password[0]) {
-        twitter_debug("screen_name or password is empty\n");
-        return;
-    }
+    /* oauth */
+    char *url0 = g_strdup_printf(TWITTER_API_BASE_URL "/1/favorites/create/%llu.xml", (long long unsigned int)id);
 
-    basic_auth = g_strdup_printf("%s:%s", screen_name, password);
-    basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth,
-                                              strlen(basic_auth));
-    g_free(basic_auth);
+    oauth_req.url = url0;
+    oauth_req.c_key = c_key;
+    oauth_req.c_sec = c_sec;
+    oauth_req.a_key = (char *)a_key;
+    oauth_req.a_sec = (char *)a_sec;
+    oauth_req.verifier = NULL;
+    oauth_req.status = NULL;
+    oauth_req.type = TYPE_POST;
+    oauth_req.count = 0;
+    oauth_req.msgid = 0;
+    oauth_req.notoken = FALSE;
 
+    oauth = make_oauth_post(&oauth_req);
 
     header = g_strdup_printf(TWITTER_FAV_POST,
                              (long long unsigned int)id,
-                             basic_auth_encoded);
-    request = g_strconcat(header, "\r\n", NULL);
+                             (int)strlen(oauth));
+
+    request = g_strconcat(header, "\r\n", oauth, NULL);
+    twitter_debug("request=%s\n", request);
 
     purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE,
                                   NULL, TRUE, request, TRUE,
                                   fav_with_api_cb, NULL);
-    twitter_debug("request = %s\n", request);
 
     g_free(header);
-    g_free(basic_auth_encoded);
+    g_free(oauth);
     g_free(request);
 }
 
@@ -628,40 +1029,58 @@
 retweet_with_api(guint64 id)
 {
     char *header, *request;
-    char *basic_auth, *basic_auth_encoded;
+    char *oauth;
+    const char *a_key = NULL;
+    const char *a_sec = NULL;
+    PurpleConversation *conv;
+    oauth_request_t oauth_req;
 
-    const char *screen_name =
-        purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER);
-    const char *password = purple_prefs_get_string(OPT_PASSWORD_TWITTER);
+    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+                                                 "[email protected]",
+                                                 account_for_twitter); /* xxx */
+
+    a_key = purple_prefs_get_string(OPT_AKEY_TWITTER);
+    a_sec = purple_prefs_get_string(OPT_ASEC_TWITTER);
+
+    if(!a_key || !a_sec) {
+        return;
+    }
 
     if(id == 0) {
         twitter_debug("invalid message id\n");
         return;
     }
 
-    if (!screen_name || !password || !screen_name[0] || !password[0]) {
-        twitter_debug("screen_name or password is empty\n");
-        return;
-    }
+    /* oauth */
+    char *url0 = g_strdup_printf(TWITTER_API_BASE_URL "/1/statuses/retweet%llu.xml", (long long unsigned int)id);
 
-    basic_auth = g_strdup_printf("%s:%s", screen_name, password);
-    basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth,
-                                              strlen(basic_auth));
-    g_free(basic_auth);
+    oauth_req.url = url0;
+    oauth_req.c_key = c_key;
+    oauth_req.c_sec = c_sec;
+    oauth_req.a_key = (char *)a_key;
+    oauth_req.a_sec = (char *)a_sec;
+    oauth_req.verifier = NULL;
+    oauth_req.status = NULL;
+    oauth_req.type = TYPE_POST;
+    oauth_req.count = 0;
+    oauth_req.msgid = 0;
+    oauth_req.notoken = FALSE;
 
+    oauth = make_oauth_post(&oauth_req);
 
     header = g_strdup_printf(TWITTER_RETWEET_POST,
                              (long long unsigned int)id,
-                             basic_auth_encoded);
-    request = g_strconcat(header, "\r\n", NULL);
+                             (int)strlen(oauth));
+
+    request = g_strconcat(header, "\r\n", oauth, NULL);
+    twitter_debug("request=%s\n", request);
 
     purple_util_fetch_url_request(TWITTER_API_BASE_URL, FALSE,
                                   NULL, TRUE, request, TRUE,
                                   retweet_with_api_cb, NULL);
-    twitter_debug("request = %s\n", request);
 
     g_free(header);
-    g_free(basic_auth_encoded);
+    g_free(oauth);
     g_free(request);
 }
 
--- a/twitter_api.h	Tue May 18 00:12:59 2010 +0900
+++ b/twitter_api.h	Thu Aug 26 15:05:33 2010 +0900
@@ -1,6 +1,45 @@
 #ifndef _PIDGIN_TWITTER_TWITTER_API_H_
 #define _PIDGIN_TWITTER_TWITTER_API_H_
 
+#include <cipher.h> /* from libpurple */
+#include <request.h>
+//#include <conversation.h>
+#include <math.h>
+
+/* twitter API specific macros */
+#define TWITTER_BASE_URL "http://twitter.com"
+#define TWITTER_API_BASE_URL "http://api.twitter.com"
+
+#define TWITTER_STATUS_GET "GET /1/statuses/home_timeline.xml?%s HTTP/1.1\r\n" \
+    "Host: api.twitter.com\r\n"                                          \
+    "User-Agent: pidgin-twitter\r\n"
+
+#define TWITTER_STATUS_POST "POST /1/statuses/update.xml HTTP/1.1\r\n" \
+    "Host: api.twitter.com\r\n"                                          \
+    "User-Agent: pidgin-twitter\r\n"                                 \
+    "Content-Length: %d\r\n"
+
+#define TWITTER_FAV_POST "POST /1/favorites/create/%llu.xml HTTP/1.1\r\n" \
+    "Host: api.twitter.com\r\n"                                          \
+    "User-Agent: pidgin-twitter\r\n" \
+    "Content-Length: %d\r\n"
+
+#define TWITTER_RETWEET_POST "POST /1/statuses/retweet/%llu.xml HTTP/1.1\r\n" \
+    "Host: api.twitter.com\r\n"                                          \
+    "User-Agent: pidgin-twitter\r\n" \
+    "Content-Length: %d\r\n"
+
+#define TWITTER_STATUS_FORMAT "&source=pidgintwitter&status=%s"
+//#define TWITTER_REPLY_FORMAT  "&source=pidgintwitter&status=%s&in_reply_to_status_id=%llu"
+
+#define TWITTER_DEFAULT_INTERVAL (60)
+#define TWITTER_OLD_DEFAULT_ICON_URL "http://static.twitter.com/images/default_profile_bigger.png"
+#define TWITTER_DEFAULT_ICON_URL "http://s.twimg.com/images/default_profile_3_bigger.png"
+
+#define TWITTER_DEFAULT_RETRIEVE_COUNT (20)
+
+
+/* prototypes */
 void post_status_with_api(PurpleAccount *account, char **buffer);
 gboolean get_status_with_api(gpointer data);
 void fav_with_api(guint64 id);