# HG changeset patch # User Yoshiki Yazawa # Date 1216310418 -32400 # Node ID 0c4a83f734cda516f67babec88276ebb79230972 # Parent 8b097fcb9243dfbf48e246952f0f4b3f0ce945c7 - duplication avoidance for the posted messages has been implemented. - time stamps in a log will be printed correctly. - some functions have been relocated in the source file. - some data definitions and macros have been moved to the header file. - minor modification to Makefile.in. diff -r 8b097fcb9243 -r 0c4a83f734cd Makefile.in --- a/Makefile.in Thu Jul 17 23:53:15 2008 +0900 +++ b/Makefile.in Fri Jul 18 01:00:18 2008 +0900 @@ -1,5 +1,6 @@ OBJECTIVE = pidgin-twitter.so SRC = pidgin-twitter.c +HDR = ${SRC:.c=.h} PIDGIN_CFLAGS = @PIDGIN_CFLAGS@ GLIB_CFLAGS = @GLIB_CFLAGS@ @@ -15,7 +16,7 @@ all: $(OBJECTIVE) -$(OBJECTIVE): $(SRC) +$(OBJECTIVE): $(SRC) $(HDR) gcc -o $@ $(SRC) $(CFLAGS) $(LDFLAGS) -g diff -r 8b097fcb9243 -r 0c4a83f734cd pidgin-twitter.c --- a/pidgin-twitter.c Thu Jul 17 23:53:15 2008 +0900 +++ b/pidgin-twitter.c Fri Jul 18 01:00:18 2008 +0900 @@ -19,260 +19,24 @@ #define PURPLE_PLUGINS 1 #include "pidgin-twitter.h" -#include - + +/***********/ /* globals */ +/***********/ static GRegex *regp[9]; static gboolean suppress_oops = FALSE; static GHashTable *icon_data_by_user = NULL; // twitter static GHashTable *icon_data_by_user2 = NULL; // wassr static GHashTable *icon_data_by_user3 = NULL; // identi.ca static GHashTable *conv_hash = NULL; - -#define WASSR_POST_LEN (255 * 4) +static GList *statuseslist = NULL; +static GList *postedlist = NULL; static gchar *wassr_post = NULL; -typedef struct _icon_data { - gint icon_id; // image id - gboolean requested; // TRUE if download icon has been requested - GList *request_list; // marker list - PurpleUtilFetchUrlData *fetch_data; // icon fetch data -} icon_data; - -enum { - unknown_service = 0, - twitter_service, - wassr_service, - identica_service -}; - -typedef struct _eval_data { - gint which; - gint service; -} eval_data; - -typedef struct _got_icon_data { - gchar *user_name; - gint service; -} got_icon_data; - -#define TWITTER_STATUS_POST "POST /statuses/update.xml HTTP/1.0\r\n" \ - "Host: twitter.com\r\n" \ - "User-Agent: Pidgin-Twitter\r\n" \ - "Authorization: Basic %s\r\n" \ - "Content-Length: %d\r\n\r\n" - -#define TWITTER_STATUS_FORMAT "status=%s" -#define TWITTER_STATUS_TERMINATOR "\r\n\r\n" - -#define TWITTER_BASE_URL "http://twitter.com" - -#define TWITTER_STATUS_GET "GET /statuses/friends_timeline.xml HTTP/1.0\r\n" \ - "Host: twitter.com\r\n" \ - "User-Agent: Pidgin-Twitter\r\n" \ - "Authorization: Basic %s\r\n" - - - - - - - -/* xml parser*/ -#include -#include -#include -#include -#include -#include - -typedef struct _status { - time_t time; - guint id; - gchar *created_at; - gchar *text; - gchar *screen_name; - gchar *profile_image_url; -} status_t; - -GList *stlist = NULL; - -static void -parse_user(xmlNode *user, status_t *st) -{ - xmlNode *nptr; - - for(nptr = user->children; nptr != NULL; nptr = nptr->next) { - if(nptr->type == XML_ELEMENT_NODE) { - if(!xmlStrcmp(nptr->name, (xmlChar *)"screen_name")) { - gchar *str = (gchar *)xmlNodeGetContent(nptr); - st->screen_name = g_strdup(str); - xmlFree(str); - } - else if(!xmlStrcmp(nptr->name, (xmlChar *)"profile_image_url")) { - gchar *str = (gchar *)xmlNodeGetContent(nptr); - st->profile_image_url = g_strdup(str); - xmlFree(str); - } - } - } -} - - -static void -parse_status(xmlNode *status) -{ - xmlNode *nptr; - - status_t *st = g_new0(status_t, 1); - - stlist = g_list_prepend(stlist, st); - - for(nptr = status->children; nptr != NULL; nptr = nptr->next) { - if(nptr->type == XML_ELEMENT_NODE) { - if(!xmlStrcmp(nptr->name, (xmlChar *)"created_at")) { - gchar *str = (gchar *)xmlNodeGetContent(nptr); - st->created_at = g_strdup(str); - xmlFree(str); - } - else if(!xmlStrcmp(nptr->name, (xmlChar *)"id")) { - gchar *str = (gchar *)xmlNodeGetContent(nptr); - st->id = atoi(str); - xmlFree(str); - } - else if(!xmlStrcmp(nptr->name, (xmlChar *)"text")) { - gchar *str = (gchar *)xmlNodeGetContent(nptr); - st->text = g_strdup(str); - xmlFree(str); - } - else if(!xmlStrcmp(nptr->name, (xmlChar *)"user")) { - parse_user(nptr, st); - } - } - } -} - - -static void -get_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, - const gchar *error_message) - -{ - xmlDocPtr doc; - xmlNode *nptr, *nptr2; - static guint lastid = 0; - g_return_if_fail(url_text != NULL); - - PurpleConversation *conv = (PurpleConversation *)user_data; - - if(!conv) - return; - - const gchar *start = strstr(url_text, "children; nptr != NULL; nptr = nptr->next) { - if(nptr->type == XML_ELEMENT_NODE && - !xmlStrcmp(nptr->name, (xmlChar *)"statuses")) { - - for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next) { - if(nptr2->type == XML_ELEMENT_NODE && - !xmlStrcmp(nptr2->name, (xmlChar *)"status")) { - parse_status(nptr2); - } - } - } - } /* for */ - - /* process stlist */ - GList *stp; - - for(stp = stlist; stp; stp=stp->next) { - status_t *st = (status_t *)stp->data; - if(st->id > lastid && - strcmp(st->screen_name, - purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER))) { - - gchar *msg = NULL; - - msg = g_strdup_printf("%s: %s\n", st->screen_name, st->text); - purple_conv_im_write(conv->u.im, - "twitter@twitter.com", - msg, - PURPLE_MESSAGE_RECV, - st->time); - lastid = st->id; - - g_free(msg); - } - - g_free(st->created_at); - g_free(st->text); - g_free(st->screen_name); - g_free(st->profile_image_url); - g_free(stp->data); - stp->data = NULL; - } - - stlist = g_list_remove_all(stlist, NULL); -} - - -/* api based get */ -static gboolean -get_status_with_api(gpointer data) -{ - /* fetch friends time line */ - char *request, *header; - char *basic_auth, *basic_auth_encoded; - - PurpleConversation *conv = (PurpleConversation *)data; - if(!conv) - return FALSE; //cease fetch - - const char *screen_name = - purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER); - const char *password = - purple_prefs_get_string(OPT_PASSWORD_TWITTER); - - 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); - - /* header */ - header = g_strdup_printf(TWITTER_STATUS_GET, basic_auth_encoded); - request = g_strconcat(header, TWITTER_STATUS_TERMINATOR, NULL); - - /* invoke fetch */ - purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, - NULL, TRUE, request, TRUE, - get_status_with_api_cb, data); - - g_free(header); - g_free(basic_auth_encoded); - g_free(request); - - return TRUE; -} - - - - - - - - + +/*************/ +/* functions */ +/*************/ /* this function is a modified clone of purple_markup_strip_html() */ static char * @@ -447,6 +211,8 @@ /**********************/ /* our implementation */ /**********************/ + +/* string utilities */ static void escape(gchar **str) { @@ -502,18 +268,238 @@ } -typedef struct twitter_message { - PurpleAccount *account; - char *status; - time_t time; -} twitter_message_t; - +/**************************/ +/* API base get functions */ +/**************************/ +/* xml parser */ +static void +parse_user(xmlNode *user, status_t *st) +{ + xmlNode *nptr; + + for(nptr = user->children; nptr != NULL; nptr = nptr->next) { + if(nptr->type == XML_ELEMENT_NODE) { + if(!xmlStrcmp(nptr->name, (xmlChar *)"screen_name")) { + gchar *str = (gchar *)xmlNodeGetContent(nptr); + st->screen_name = g_strdup(str); + xmlFree(str); + } + else if(!xmlStrcmp(nptr->name, (xmlChar *)"profile_image_url")) { + gchar *str = (gchar *)xmlNodeGetContent(nptr); + st->profile_image_url = g_strdup(str); + xmlFree(str); + } + } + } +} + +static void +parse_status(xmlNode *status, status_t *st) +{ + xmlNode *nptr; + + for(nptr = status->children; nptr != NULL; nptr = nptr->next) { + if(nptr->type == XML_ELEMENT_NODE) { + if(!xmlStrcmp(nptr->name, (xmlChar *)"created_at")) { + gchar *str = (gchar *)xmlNodeGetContent(nptr); + st->created_at = g_strdup(str); + + /* set locale to C */ + gchar *lc_time = setlocale(LC_TIME, NULL); + setlocale(LC_TIME, "C"); + + /* set timezone to UTC */ + gchar *timezone = g_strdup(getenv("TZ")); + setenv("TZ", "UTC", TRUE); + tzset(); + + /* read time stamp */ + struct tm res; + strptime(str, "%a %b %d %T %z %Y", &res); + st->time = mktime(&res); + + /* restore timezone */ + if(timezone) + setenv("TZ", timezone, TRUE); + else + unsetenv("TZ"); + + g_free(timezone); + + /* restore locale */ + setlocale(LC_TIME, lc_time); + + xmlFree(str); + } + else if(!xmlStrcmp(nptr->name, (xmlChar *)"id")) { + gchar *str = (gchar *)xmlNodeGetContent(nptr); + st->id = atoi(str); + xmlFree(str); + } + else if(!xmlStrcmp(nptr->name, (xmlChar *)"text")) { + gchar *str = (gchar *)xmlNodeGetContent(nptr); + st->text = g_strdup(str); + xmlFree(str); + } + else if(!xmlStrcmp(nptr->name, (xmlChar *)"user")) { + parse_user(nptr, st); + } + } + } +} + +static void +free_status(status_t *st) +{ + g_free(st->created_at); + g_free(st->text); + g_free(st->screen_name); + g_free(st->profile_image_url); +} + +static gboolean +is_posted_message(status_t *status) +{ + GList *pp; + gboolean rv = FALSE; + + for(pp = postedlist; pp; pp=pp->next) { + status_t *posted = (status_t *)pp->data; + if(posted->id == status->id) { + rv = TRUE; + free_status(posted); + g_free(pp->data); + pp->data = NULL; + } + } + + postedlist = g_list_remove_all(postedlist, NULL); + + return rv; +} + +static void +get_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *url_text, size_t len, + const gchar *error_message) + +{ + xmlDocPtr doc; + xmlNode *nptr, *nptr2; + static guint lastid = 0; + PurpleConversation *conv; + GList *stp; + const gchar *start; + + g_return_if_fail(url_text != NULL); + + conv = (PurpleConversation *)user_data; + if(!conv) + return; + + /* skip to the beginning of xml */ + start = strstr(url_text, "children; nptr != NULL; nptr = nptr->next) { + if(nptr->type == XML_ELEMENT_NODE && + !xmlStrcmp(nptr->name, (xmlChar *)"statuses")) { + + for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next) { + if(nptr2->type == XML_ELEMENT_NODE && + !xmlStrcmp(nptr2->name, (xmlChar *)"status")) { + status_t *st = g_new0(status_t, 1); + statuseslist = g_list_prepend(statuseslist, st); + parse_status(nptr2, st); + } + } + } + } + + /* process statuseslist */ + for(stp = statuseslist; stp; stp=stp->next) { + status_t *st = (status_t *)stp->data; + + if(st->id > lastid && !is_posted_message(st)) { + gchar *msg = NULL; + + msg = g_strdup_printf("%s: %s\n", st->screen_name, st->text); + purple_conv_im_write(conv->u.im, + "twitter@twitter.com", + msg, + PURPLE_MESSAGE_RECV, + st->time); + lastid = st->id; + + g_free(msg); + } + + free_status(st); + g_free(stp->data); + stp->data = NULL; + } + + statuseslist = g_list_remove_all(statuseslist, NULL); +} + +/* status fetching function. it will be called periodically. */ +static gboolean +get_status_with_api(gpointer data) +{ + /* fetch friends time line */ + char *request, *header; + char *basic_auth, *basic_auth_encoded; + + twitter_debug("called\n"); + + PurpleConversation *conv = (PurpleConversation *)data; + if(!conv) + return FALSE; //cease fetch + + const char *screen_name = + purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER); + const char *password = + purple_prefs_get_string(OPT_PASSWORD_TWITTER); + + 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); + + /* header */ + header = g_strdup_printf(TWITTER_STATUS_GET, basic_auth_encoded); + request = g_strconcat(header, TWITTER_STATUS_TERMINATOR, NULL); + + /* invoke fetch */ + purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, + NULL, TRUE, request, TRUE, + get_status_with_api_cb, data); + + g_free(header); + g_free(basic_auth_encoded); + g_free(request); + + return TRUE; +} + +/***************************/ +/* API base post functions */ +/***************************/ static void post_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message) { - twitter_message_t *tm = (struct twitter_message *)user_data; + twitter_message_t *tm = (twitter_message_t *)user_data; gchar *msg = NULL; char *p1 = NULL, *p2 = NULL; int error = 1; @@ -589,6 +575,31 @@ purple_conv_im_write(conv->u.im, purple_account_get_username(tm->account), tm->status, PURPLE_MESSAGE_SEND, tm->time); + + /* cache message ID that posted via API */ + gchar *start = NULL; + xmlDocPtr doc; + xmlNode *nptr; + + start = strstr(url_text, "children; nptr != NULL; nptr = nptr->next) { + if(nptr->type == XML_ELEMENT_NODE && + !xmlStrcmp(nptr->name, (xmlChar *)"status")) { + status_t *st = g_new0(status_t, 1); + postedlist = g_list_prepend(postedlist, st); + parse_status(nptr, st); + } + } + } else { gchar *m; m = g_strdup_printf("%s
%s", @@ -661,6 +672,9 @@ } +/***********************/ +/* intrinsic functions */ +/***********************/ static gboolean sending_im_cb(PurpleAccount *account, char *recipient, char **buffer, void *data) @@ -738,7 +752,7 @@ twitter_debug("unknown service\n"); break; } - snprintf(sub, 128, format, match, match); + g_snprintf(sub, 128, format, match, match); g_free(match); } else if(which == SENDER) { @@ -761,7 +775,7 @@ break; } - snprintf(sub, 128, format, match1 ? match1: "", match2, match2); + g_snprintf(sub, 128, format, match1 ? match1: "", match2, match2); g_free(match1); g_free(match2); @@ -771,7 +785,7 @@ gchar *match2 = g_match_info_fetch(match_info, 2); //channel const gchar *format = CHANNEL_FORMAT_WASSR; - snprintf(sub, 128, format, match1 ? match1: "", match2, match2); + g_snprintf(sub, 128, format, match1 ? match1: "", match2, match2); g_free(match1); g_free(match2); @@ -1050,7 +1064,10 @@ gint service = get_service_type(conv); switch(service) { case twitter_service: - g_source_remove_by_user_data((gpointer)conv); +#if 0 + if(purple_prefs_get_bool(OPT_API_BASE_POST)) + g_source_remove_by_user_data((gpointer)conv); +#endif detach_from_conv(conv, NULL); break; case wassr_service: @@ -1153,6 +1170,8 @@ { GList *list; + twitter_debug("called\n"); + /* find twitter conv window out and attach to that */ for(list = pidgin_conv_windows_get_list(); list; list = list->next) { PidginWindow *win = list->data; @@ -1162,9 +1181,13 @@ /* only attach to twitter conversation window */ switch(service) { case twitter_service: +#if 0 /* api based retrieve */ //xxx should configurable - get_status_with_api((gpointer)conv); - g_timeout_add_seconds(60, get_status_with_api, (gpointer)conv); + if(purple_prefs_get_bool(OPT_API_BASE_POST)) { + get_status_with_api((gpointer)conv); + g_timeout_add_seconds(60, get_status_with_api, (gpointer)conv); + } +#endif attach_to_conv(conv, NULL); break; case wassr_service: @@ -1331,6 +1354,9 @@ conv_created_cb(PurpleConversation *conv, gpointer null) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + + twitter_debug("called\n"); + g_return_if_fail(gtkconv != NULL); gint service = get_service_type(conv); @@ -1338,8 +1364,12 @@ switch(service) { case twitter_service: /* api based retrieve */ //xxx should configurable - get_status_with_api((gpointer)conv); - g_timeout_add_seconds(60, get_status_with_api, (gpointer)conv); +#if 1 + if(purple_prefs_get_bool(OPT_API_BASE_POST)) { + get_status_with_api((gpointer)conv); + g_timeout_add_seconds(60, get_status_with_api, (gpointer)conv); + } +#endif attach_to_conv(conv, NULL); break; case wassr_service: @@ -1357,6 +1387,8 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + twitter_debug("called\n"); + g_return_if_fail(gtkconv != NULL); gint service = get_service_type(conv); @@ -1365,6 +1397,8 @@ /* only attach to twitter conversation window */ switch(service) { case twitter_service: + if(purple_prefs_get_bool(OPT_API_BASE_POST)) + g_source_remove_by_user_data((gpointer)conv); hash = icon_data_by_user; break; case wassr_service: @@ -1415,10 +1449,10 @@ return FALSE; } - /* if we use api, discard incoming IM message. XXX need fix */ + /* if we use api, discard incoming IM message. XXX too wild? */ if(purple_prefs_get_bool(OPT_API_BASE_POST)) { - g_free(*sender); *sender = NULL; - g_free(*buffer); *buffer = NULL; + g_free(*sender); *sender = NULL; + g_free(*buffer); *buffer = NULL; } if(!suppress_oops || !purple_prefs_get_bool(OPT_SUPPRESS_OOPS)) @@ -1482,7 +1516,6 @@ } /* insert icon to the mark */ - gtk_text_buffer_get_iter_at_mark(target_buffer, &insertion_point, requested_mark); @@ -1573,6 +1606,7 @@ g_free(gotdata); } +/* this function will be called when profile page has been retrieved */ static void got_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) @@ -1654,7 +1688,7 @@ return dest; } - +/* this function will be called when requested icon has been retrieved */ static void got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) @@ -2474,12 +2508,12 @@ /****************/ /* API heading */ /****************/ - pref = purple_plugin_pref_new_with_label("API Based Post"); + pref = purple_plugin_pref_new_with_label("API Based Twitter Access"); purple_plugin_pref_frame_add(frame, pref); /* post configuration */ pref = purple_plugin_pref_new_with_name_and_label(OPT_API_BASE_POST, - "Post Status to Twitter via API"); + "Post/get statuses via API"); purple_plugin_pref_frame_add(frame, pref); purple_prefs_connect_callback(plugin, OPT_API_BASE_POST, diff -r 8b097fcb9243 -r 0c4a83f734cd pidgin-twitter.h --- a/pidgin-twitter.h Thu Jul 17 23:53:15 2008 +0900 +++ b/pidgin-twitter.h Fri Jul 18 01:00:18 2008 +0900 @@ -1,11 +1,17 @@ #ifndef _PIDGIN_TWITTER_H_ #define _PIDGIN_TWITTER_H_ +#define _XOPEN_SOURCE 600 #include #include #include #include #include +#include +#include + +#include +#include #include "gtkplugin.h" #include "util.h" @@ -29,6 +35,52 @@ IMAGE_IDENTICA }; +/* service id */ +enum { + unknown_service = 0, + twitter_service, + wassr_service, + identica_service +}; + +/* container to hold icon data */ +typedef struct _icon_data { + gint icon_id; // image id + gboolean requested; // TRUE if download icon has been requested + GList *request_list; // marker list + PurpleUtilFetchUrlData *fetch_data; // icon fetch data +} icon_data; + +/* used by got_icon_cb */ +typedef struct _got_icon_data { + gchar *user_name; + gint service; +} got_icon_data; + +/* used by eval */ +typedef struct _eval_data { + gint which; + gint service; +} eval_data; + +/* container for api based retrieve */ +typedef struct _status { + gchar *created_at; + gchar *text; + gchar *screen_name; + gchar *profile_image_url; + time_t time; + guint id; +} status_t; + +/* container for api based post */ +typedef struct twitter_message { + PurpleAccount *account; + char *status; + time_t time; +} twitter_message_t; + + #define PLUGIN_ID "gtk-honeyplanet-pidgin_twitter" #define PLUGIN_NAME "pidgin-twitter" @@ -79,6 +131,26 @@ #define P_CHANNEL "^(.*?[A-Za-z0-9_]+: \\r?\\n?#)([A-Za-z0-9_]+) " #define P_IMAGE_IDENTICA "\"[A-Za-z0-0_]+\"/" +/* twitter API specific macros */ +#define TWITTER_STATUS_POST "POST /statuses/update.xml HTTP/1.0\r\n" \ + "Host: twitter.com\r\n" \ + "User-Agent: Pidgin-Twitter\r\n" \ + "Authorization: Basic %s\r\n" \ + "Content-Length: %d\r\n\r\n" + +#define TWITTER_STATUS_FORMAT "status=%s" +#define TWITTER_STATUS_TERMINATOR "\r\n\r\n" + +#define TWITTER_BASE_URL "http://twitter.com" + +#define TWITTER_STATUS_GET "GET /statuses/friends_timeline.xml HTTP/1.0\r\n" \ + "Host: twitter.com\r\n" \ + "User-Agent: Pidgin-Twitter\r\n" \ + "Authorization: Basic %s\r\n" + +/* wassr specific macros */ +#define WASSR_POST_LEN (255 * 4) + /* debug macros */ #define twitter_debug(fmt, ...) purple_debug(PURPLE_DEBUG_INFO, PLUGIN_NAME, "%s():%4d: " fmt, __FUNCTION__, (int)__LINE__, ## __VA_ARGS__); #define twitter_error(fmt, ...) purple_debug(PURPLE_DEBUG_ERROR, PLUGIN_NAME, "%s():%4d: " fmt, __FUNCTION__, (int)__LINE__, ## __VA_ARGS__); @@ -123,4 +195,9 @@ static gint get_service_type(PurpleConversation *conv); static GdkPixbuf *make_scaled_pixbuf(const gchar *url_text, gsize len); +static void parse_user(xmlNode *user, status_t *st); +static void parse_status(xmlNode *status, status_t *st); +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 gboolean get_status_with_api(gpointer data); + #endif