# HG changeset patch # User Yoshiki Yazawa # Date 1282802733 -32400 # Node ID 0fe895195132ae74a37f4980f5e15f831e4d710e # Parent ff078879e68e359d28e70eefed51743b5c52e240 implement OAuth authentication. diff -r ff078879e68e -r 0fe895195132 pidgin-twitter.h --- 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) diff -r ff078879e68e -r 0fe895195132 twitter_api.c --- 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, "twitter@twitter.com", + 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, + "twitter@twitter.com", + 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, + "twitter@twitter.com", + 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); } diff -r ff078879e68e -r 0fe895195132 twitter_api.h --- 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 /* from libpurple */ +#include +//#include +#include + +/* 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);